Compare commits

..
1 Commits
Author SHA1 Message Date
John Colvin 709334a1aa test 2023-07-07 17:47:07 -04:00
30 changed files with 206 additions and 972 deletions

No files matched your search

@@ -116,7 +116,7 @@
_timer.start();
_analytics_service.archive_theos(archived_theos);
_timer.stop();
Log.Gen.info($"SingleVolSurfaceGateway._on_bb_data archiving theos vs_id: {_volsurface_id} size: " +
Log.Gen.info($"SingleVolSurfaceGateway._on_bb_data archiving theos size: " +
$"{archived_theos.SecTheo.Count} time: {_timer.lastIntervalTime} msecs");
}
} catch (Exception ex) {
@@ -160,8 +160,8 @@ namespace mbts.spark {
public static IEnumerable<bts.api.SecurityTuple> get_vs_sectups(
int vs_id, ThreadedBlackboard bb) {
var vs_usage_list = bb.read_state(SparkTags.VolSurfaceUsageTag);
return (vs_usage_list?.get_sectups(vs_id)).empty_if_null();
VolSurfaceUsageListImmutable vs_usage_list = bb.read_state(SparkTags.VolSurfaceUsageTag);
return vs_usage_list.get_sectups(vs_id);
}
@@ -242,4 +242,4 @@ namespace mbts.spark {
}
#endregion
}
}
}
@@ -60,18 +60,16 @@ namespace mbts.spark {
private IReadOnlyDictionary<string, OptionTheoInterpolationData>
_filter_for_publish(IReadOnlyDictionary<string, OptionTheoInterpolationData> all_theos) {
// find the symbol vs vs_id, get the committed vs, see if it is
// owned by the current user, and only then publish it
HashSet<string> syms = new HashSet<string>();
var usage = _bb.read_state(SparkTags.VolSurfaceUsageTag);
VolSurfaceUsageListImmutable usage = _bb.read_state(SparkTags.VolSurfaceUsageTag);
string current_user = _bb.read_state(SparkTags.CurrentUser).name;
foreach (var u in (usage?.UsageList).empty_if_null()) {
foreach (var u in usage.UsageList) {
VolSurfaceInput vs_input =
_bb.read_state(SparkTags.VolSurfaceCommittedInputTag(u.VsId));
if (vs_input != null) {
if (vs_input.surface_settings.core_settings.vs_manager ==
current_user) {
syms.Add(u.SecTup.Symbol);
syms.Add(u.SymbolName);
}
}
}
@@ -83,16 +81,14 @@ namespace mbts.spark {
SystemContractData sc_data = _bb.read_state(SparkTags.SystemContractsTag, null);
IReadOnlyDictionary<string, OptionTheoInterpolationData> all_theos =
_bb.read_state(SparkTags.AllInterpGreeks, null);
all_theos = _filter_for_publish(all_theos);
// find the symbol vs vs_id, get the committed vs, see if it is
// owned by the current user, and only then publish it
IReadOnlyDictionary<string, OptionDriverPriceMap> all_drivers =
_bb.read_state(SparkTags.AllOptionDriverBBOTag, null);
if (sc_data == null || all_theos == null || all_drivers == null)
return;
_timer.start();
all_theos = _filter_for_publish(all_theos);
if (all_theos.Count == 0) {
bts.Log.Gen.info($"InterpTheosServerPublisher._publish_data all_theos.Count == 0");
return;
}
IDictionary<UInt32, DriverMD> driver_map = _get_drivers(all_drivers, sc_data);
IEnumerable<Tuple<SecurityImmutable, TheoPoint>> theo_map = _get_theos(all_theos, sc_data);
// assemble it together and send it to the server
@@ -115,7 +111,6 @@ namespace mbts.spark {
ptp.Vega = is_call ? tp.cvega : tp.pvega;
ptp.Theta = is_call ? tp.c_theta : tp.p_theta;
ptp.Rho = Double.NaN;
ptp.Vol = tp.vol;
ptp_list.Add(ptp);
}
bts.api.orderman.PrecomputedTheos server_theos = new PrecomputedTheos();
@@ -126,7 +121,7 @@ namespace mbts.spark {
_order_service.set_precomputed_theos(server_theos);
_timer.stop();
var pub_time = _timer.lastIntervalTime;
bts.Log.Gen.info($"InterpTheosServerPublisher._publish_data symbols: {all_theos.Count} " +
bts.Log.Gen.info($"InterpTheosServerPublisher._publish_data " +
$"size: {server_theos.Theos.Count} comp_time: {comp_time} msecs " +
$"pub_time: {pub_time}");
}
@@ -135,4 +130,4 @@ namespace mbts.spark {
_bb.data_callback -= _on_data;
}
}
}
}
@@ -22,7 +22,7 @@ namespace mbts.spark {
// ii. IntepGreeks for each vs
public class VolSurfaceInterpGreeksMultiplexer : IDisposable {
private readonly ThreadedBlackboard _bb;
private readonly ITag<VolSurfaceUsageListImmutable?> _in_bb_vs_usage_tag;
private readonly ITag<VolSurfaceUsageListImmutable> _in_bb_vs_usage_tag;
private IDictionary<int, ITag<VSTheos>> _vs_theo_tags =
new Dictionary<int, ITag<VSTheos>>();
private VolSurfaceUsageListImmutable _vs_usage_map =
@@ -48,8 +48,8 @@ namespace mbts.spark {
}
private void _analyze_usages() {
_vs_usage_map = _bb.read_state(_in_bb_vs_usage_tag) ??
new VolSurfaceUsageList().immutable();
_vs_usage_map = _bb.read_state(_in_bb_vs_usage_tag,
new VolSurfaceUsageListImmutable(new VolSurfaceUsageList()));
bts.Log.Gen.info($"VolSurfaceInterpGreeksMultiplexer._analyze_usages " +
$"count: {_vs_usage_map.UsageList.Count()}");
foreach (int vs_id in _vs_usage_map.vss()) {
@@ -40,8 +40,10 @@ namespace mbts.analytics {
/// last computed min/max driver for each exp
private IDictionary<VolSurfaceExpiration, Tuple<double, double>>
_computed_driver_range_map = new Dictionary<VolSurfaceExpiration, Tuple<double, double>>();
private readonly IEnumerable<double> _default_abs_moves = config.Configuration.get<bts.config.VolSurface>().default_abs_moves;
private readonly IEnumerable<double> _default_abs_moves =
new double[] { 300, -200, -125, -100, -75, -50, -25, 0, 25, 50, 75, 100, 125, 200, 300 };
private readonly double _cushion_percentage = 25.0;
public event
Action<IDictionary<VolSurfaceExpiration, IEnumerable<Theos>>, double, double> on_theos =
(o, vt, tt) => { };
@@ -50,7 +52,7 @@ namespace mbts.analytics {
(o, vt, tt) => { };
public VolSurfaceInterpGreeksProducer(int vs_id) {
Log.Gen.info($"VolSurfaceInterpGreeksProducer.cons vs_id: {vs_id}");
Log.Gen.info($"VolSurfaceInterpGreeksProducer.cons vs_id: {vs_id}");
_vs_id = vs_id;
}
@@ -1281,7 +1281,7 @@ namespace mbts.spark {
if (!_settings.piq_column.show)
return;
int origin_index = LadderRender.index_of(_price_list, _mkt_break_lines.origin, _price_comparer);
LadderRender.render_piqs(_piq_vis_tup.visual, _last_vis_tup.glyph_context, _price_list, _vm.price_vs_piq, _settings.row_height, _spec, _settings.piq_column, _fmt_mkt_quantity);
LadderRender.render_piqs(_piq_vis_tup.visual, _last_vis_tup.glyph_context, origin_index, _price_list, _vm.price_vs_piq, _settings.row_height, _spec, _settings.piq_column, _fmt_mkt_quantity);
}
void _refresh_sizes() {
@@ -368,32 +368,30 @@ namespace mbts.spark {
public delegate void RenderCell<T>(DrawingContext dc, int row, T data_item);
static void _render_cells<T>(DrawingVisual dv, double row_height, IEnumerable<(double price, T value)> cells, LadderColumnSettings settings, IList<double> prices, RegionalTickFactors spec, RenderCell<T> render_cell) {
static void _render_cells<T>(DrawingVisual dv, double row_height, IEnumerable<(double price, T value)> data, LadderColumnSettings settings, IList<double> prices, RegionalTickFactors spec, RenderCell<T> render_cell) {
using (var dc = dv.RenderOpen())
using (_draw_with_clip(dc, settings, row_height, prices.Count)) {
if (prices.is_null_or_empty() || cells.is_null_or_empty()) {
if (data.is_null_or_empty())
return;
}
var min_price = prices[prices.Count - 1];
var max_price = prices[0];
Log.assert(min_price.is_real() && max_price.is_real() && min_price <= max_price);
var indexed_cells = cells
.Where(item => item.price.is_real() && item.price.is_within_range(min_price, max_price))
.Select(row => {
checked {
return (row.price, row.value, (int)spec.tickdiff(max_price, row.price));
}
});
foreach (var (price, value, idx) in indexed_cells) {
var check_price = spec.tickdown(max_price, idx);
Log.assert(spec.tick_equals(price, check_price));
Log.assert(idx >= 0 && idx < prices.Count);
render_cell(dc, idx, value);
int curr_index = 0;
double curr_price = prices[curr_index];
foreach (var (price, value) in data) {
if (double.IsNaN(price))
continue;
// prices are in descending order, so get diff from curr_price - price
var tick_diff = spec.tickdiff(curr_price, price) ?? 0;
Log.assert(
tick_diff > (int.MaxValue - curr_index),
$"Unable to index ladder row for price {price}: tick count exceeds int.MaxValue");
curr_index += (int)tick_diff;
curr_price = price;
if (curr_index >= 0 && curr_index < prices.Count) {
render_cell(dc, curr_index, value);
} else break;
}
}
}
internal static void render_last(DrawingVisual dv, GlyphContext glyph_context, NativePriceSize last, int index, double row_height, LadderColumnSettings settings, Func<uint, string> fmt_value) {
if (fmt_value is null)
fmt_value = FormattingDelegates.default_string<uint>();
@@ -407,18 +405,38 @@ namespace mbts.spark {
internal static void render_piqs(
DrawingVisual dv,
GlyphContext glyph_context,
int origin_index,
IList<double> prices,
IEnumerable<(double price, uint piq)> pvp,
double row_height,
RegionalTickFactors spec,
LadderColumnSettings settings,
Func<uint, string> fmt_value
) {
fmt_value ??= FormattingDelegates.default_string<uint>();
var fg = settings.foreground.rgb_brush();
_render_cells(dv, row_height, pvp, settings, prices, spec, (dc, row, piq) =>
dc.DrawGlyphRun(
fg, format_text(fmt_value(piq), row, row_height, settings, glyph_context)));
Func<uint, string> fmt_value) {
//
using (var dc = dv.RenderOpen()) {
using (_draw_with_clip(dc, settings, row_height, prices.Count)) {
if (!prices.within_range(origin_index) || pvp.is_null_or_empty())
return;
if (fmt_value is null)
fmt_value = FormattingDelegates.default_string<uint>();
var foreground = settings.foreground.rgb_brush();
int curr_index = origin_index;
double curr_price = prices[curr_index];
foreach (var entry in pvp) {
double price = entry.price;
// prices are in descending order, so get diff from curr_price - price
var tick_diff = spec.tickdiff(curr_price, price).assert_value();
checked {
curr_index += (int)tick_diff;
}
curr_price = price;
if (curr_index >= 0 && curr_index < prices.Count) {
var gr = format_text(fmt_value(entry.piq), curr_index, row_height, settings, glyph_context);
dc.DrawGlyphRun(foreground, gr);
} else break;
}
}
}
}
public static void render_mkt_break(DrawingVisual dv, int index, double row_height, uint color) =>
@@ -59,9 +59,7 @@ namespace bts.shell.manual_fills {
nameof(quantity_display),
() => {
var sec_meta = security.HasValue ? _sec_meta_store.get(security.Value) : null;
return sec_meta?.HasQuantityDisplay ?? false ?
sec_meta.Value.QuantityDisplay :
Security.Defaults.quantity_display_settingsi;
return sec_meta?.QuantityDisplay ?? Security.Defaults.quantity_display_settingsi;
});
// TODO (prob): remove conversion_data. Replace get_qty_display_divisor delegates w/
// TickDisplaySettingsImmutable for qty, obtained via ISecurityMetadataStore.
@@ -293,10 +293,7 @@ namespace bts.shell.pnl {
return _quantity_display_backing.Value;
}
if (security.HasValue) {
var sec_meta = _sec_meta_store.get(security.Value);
if (sec_meta?.HasQuantityDisplay ?? false) {
_quantity_display_backing = sec_meta.Value.QuantityDisplay;
}
_quantity_display_backing = _sec_meta_store.get(security.Value)?.QuantityDisplay;
}
return _quantity_display_backing ??= Security.Defaults.quantity_display_settingsi;
}
@@ -121,7 +121,7 @@ namespace bts.shell {
void _on_orders(TableUpdate<(SecurityImmutable? sec, OrderRecordImmutable order), uint> update) {
// must update notification_service_data_cache first, some of the predicate/triggers (esp. ToastNotifications) will use the notification_service_data_cache to lookup order-history
_data_cache.update(update);
_data_cache.update(update.as_iincremental());
foreach (var o in update.added.Concat(update.changed))
_order_notifications.process_item(o);
}
@@ -1,157 +1,89 @@
using bts.api;
using bts.api.core;
using bts.api.orderman;
using mbts.spark;
using mbts.utils;
using System;
using System.Collections.Generic;
using System.Linq;
namespace bts.shell {
// Any predicate-sound/notifications that need to reference previous data (i.e. notifications based on change/time, or state changes) should use the INotificationServiceDataCache.
// The INotificationService updates these caches in its IObservable.OnNext callbacks, just before calling all of the predicate-trigger methods.
// Therfore, data referenced through this interface is guarenteed to be up-to-date, and thread safe. It also reduces performance overhead by avoiding redundant bookeeping.
// Note: last_order_reset_time can be used by sound/toast-notification sources to avoid sending false-positive notifications for old orders (i.e. all the orders that arrive in the first update on startup/server restart)
// --------------------------------------------------------------------------------------------------------------------------------------------
// IMPOARTANT!!! The current implementation of this interface is NOT INTENDED FOR GENERAL USE - order_history is NOT THREAD-SAFE
interface INotificationServiceDataCache {
bool is_first_order_update { get; }
IReadOnlyDictionary<uint, List<TimeStampedData<OrderSecurity>>> order_history { get; }
}
static class NotificationServiceDataCacheExtensions {
public static OrderSecurity? get_previous_order_state(this INotificationServiceDataCache notification_service_data_cache, uint key) {
if (notification_service_data_cache.order_history.TryGetValue(key, out List<TimeStampedData<OrderSecurity>> l)) {
if (l.Count > 1) {
return l[l.Count - 2].data;
}
}
return null;
}
}
struct TimeStampedData<T> {
public readonly T data;
public readonly DateTime time_stamp;
public TimeStampedData(T data, DateTime time_stamp) {
this.data = data;
this.time_stamp = time_stamp;
}
}
partial class NotificationService : INotificationService, IDisposable {
class DataCache : INotificationServiceDataCache {
const int _ORDER_HISTORY_BUFFER_SIZE = 10;
const int _TABLE_HISTORY_BUFFER_SIZE = 5;
readonly Dictionary<uint, OrderSecurity> _order_cache = new Dictionary<uint, OrderSecurity>();
readonly Dictionary<uint, List<TimeStampedData<OrderSecurity>>> _order_history = new Dictionary<uint, List<TimeStampedData<OrderSecurity>>>();
readonly List<(TableUpdate<(SecurityImmutable?, OrderRecordImmutable), uint>, DateTime)> _table_history = new(_TABLE_HISTORY_BUFFER_SIZE);
readonly object _lock = new();
public IReadOnlyDictionary<uint, List<TimeStampedData<OrderSecurity>>> order_history => _order_history;
public bool is_first_order_update { get; private set; }
internal void update(TableUpdate<(SecurityImmutable? sec, OrderRecordImmutable order), uint> table) {
// First, try processing the update per usual (incrementally). If it throws,
// then log the exception and reset the state.
DateTime now = DateTime.Now;
lock (_lock) {
_circular_buffer_add(_table_history, (table, now), _TABLE_HISTORY_BUFFER_SIZE);
try {
impl();
return;
} catch (Exception ex) {
var table_history_desc = _fmt_order_table_history_safe();
Log.Ui.error(
$"Error processing order update in NotificationService: {ex}.\n" +
$"***Last table updates below***\n" +
table_history_desc);
}
// This is the 'escape' path. It really shouldn't throw.
reset();
}
void impl() {
if (table.state.Count == 0) {
_order_cache.Clear();
}
is_first_order_update = _order_cache.Count == 0 && table.state.Count > 0;
foreach (var order in table.removed) {
var k = table.key(order);
_order_cache.Remove(k);
_order_history.Remove(k);
}
foreach (var ch in table.changes) {
var k = table.key(ch.New);
_order_cache[k] = ch.New;
_order_history[k].Add(new(ch.New, now));
}
foreach (var order in table.added) {
var k = table.key(order);
_order_cache[k] = order;
var hist = _order_history.GetOrAdd(k, _ => new());
_circular_buffer_add(hist, new(order, now), _ORDER_HISTORY_BUFFER_SIZE);
}
}
void reset() {
_order_cache.Clear();
_order_history.Clear();
_table_history.Clear();
foreach (var (k, order) in table.state) {
_order_cache[k] = order;
_order_history.GetOrAdd(k, _ => new()).Add(new(order, now));
}
is_first_order_update = true;
}
}
static void _circular_buffer_add<T>(List<T> list, T item, int buffer_size) {
Log.assert(buffer_size > 0);
if (list.Count > buffer_size - 1) {
// Remove oldest items (trim front)
list.RemoveRange(
0, list.Count - (buffer_size - 1));
}
list.Add(item);
}
string _fmt_order_table_history_safe() {
return _fmt_table_history_safe(_table_history, fmt_order_tup);
static string fmt_order_tup((SecurityImmutable?, OrderRecordImmutable) order_tup) {
var (sec, order) = order_tup;
var order_desc = OrderUtils.short_description(order, sec.spec_or_default());
var sec_desc = sec?.ToString() ?? ("sid=" + order.security_key().ToString());
return $"{sec_desc} {order_desc}";
}
}
static string _fmt_table_history_safe<T, TKey>(
List<(TableUpdate<T, TKey>, DateTime)> table_history, Func<T, string> fmt_value) where TKey : IEquatable<TKey> {
var table_history_desc = new System.Text.StringBuilder();
for (int i = 0; i < table_history.Count; i++) {
var (table, time_stamp) = table_history[i];
table_history_desc.AppendLine(
$"Update - {table_history.Count - i}, at {time_stamp:yyyy-MM-dd hh:mm:ss.ffffff}");
table_history_desc.AppendLine(_fmt_table_safe(table, fmt_value));
}
return table_history_desc.ToString();
}
static string _fmt_table_safe<T, TKey>(
TableUpdate<T, TKey> table, Func<T, string> fmt_value) where TKey : IEquatable<TKey> {
try {
return table.description(fmt_value);
} catch (Exception ex) {
return $"Exception while formatting TableUpdate: ${ex}";
}
}
}
}
}
using bts.api;
using bts.api.orderman;
using mbts.spark;
using mbts.utils;
using System;
using System.Collections.Generic;
namespace bts.shell {
// Any predicate-sound/notifications that need to reference previous data (i.e. notifications based on change/time, or state changes) should use the INotificationServiceDataCache.
// The INotificationService updates these caches in its IObservable.OnNext callbacks, just before calling all of the predicate-trigger methods.
// Therfore, data referenced through this interface is guarenteed to be up-to-date, and thread safe. It also reduces performance overhead by avoiding redundant bookeeping.
// Note: last_order_reset_time can be used by sound/toast-notification sources to avoid sending false-positive notifications for old orders (i.e. all the orders that arrive in the first update on startup/server restart)
// --------------------------------------------------------------------------------------------------------------------------------------------
// IMPOARTANT!!! The current implementation of this interface is NOT INTENDED FOR GENERAL USE - order_history is NOT THREAD-SAFE
interface INotificationServiceDataCache {
DateTime last_order_reset_time { get; }
bool is_first_order_update { get; }
IReadOnlyDictionary<uint, List<TimeStampedData<OrderSecurity>>> order_history { get; }
}
static class NotificationServiceDataCacheExtensions {
public static OrderSecurity? get_previous_order_state(this INotificationServiceDataCache notification_service_data_cache, uint key) {
if (notification_service_data_cache.order_history.TryGetValue(key, out List<TimeStampedData<OrderSecurity>> l)) {
if (l.Count > 1) {
return l[l.Count - 2].data;
}
}
return null;
}
}
struct TimeStampedData<T> {
public readonly T data;
public readonly DateTime time_stamp;
public TimeStampedData(T data, DateTime time_stamp) {
this.data = data;
this.time_stamp = time_stamp;
}
}
partial class NotificationService : INotificationService, IDisposable {
class DataCache : INotificationServiceDataCache {
const int _buffer_size = 10;
readonly Dictionary<uint, OrderSecurity> _order_cache = new Dictionary<uint, OrderSecurity>();
readonly Dictionary<uint, List<TimeStampedData<OrderSecurity>>> _order_history = new Dictionary<uint, List<TimeStampedData<OrderSecurity>>>();
public IReadOnlyDictionary<uint, List<TimeStampedData<OrderSecurity>>> order_history => _order_history;
public DateTime last_order_reset_time { get; private set; }
public bool is_first_order_update { get; private set; }
internal void update(IIncrementalUpdate<(SecurityImmutable? sec, OrderRecordImmutable order), uint> data) {
DateTime now = DateTime.Now;
if (data.removed.Count == _order_cache.Count) {
last_order_reset_time = now;
_order_cache.Clear();
}
is_first_order_update = _order_cache.Count == 0 && data.added.Count > 0;
foreach (var kv in data.removed) {
if (data.added.ContainsKey(kv.Key)) {
_order_cache[kv.Key] = kv.Value;
_order_history[kv.Key].Add(new TimeStampedData<OrderSecurity>(kv.Value, now));
} else {
_order_cache.Remove(kv.Key);
// TODO: maybe these shouldn't be removed?
// if we add delta/time predicates that need to refer to order history, we might need to keep them around
// although, if the server is telling us to remove them, perhaps we shouldn't consider such data.
_order_history.Remove(kv.Key);
}
}
foreach (var kv in data.added) {
if (data.removed.ContainsKey(kv.Key))
continue;
_order_cache.Add(kv.Key, kv.Value);
_order_history.AddOrUpdate(kv.Key,
k => new List<TimeStampedData<OrderSecurity>> { new TimeStampedData<OrderSecurity>(kv.Value, now) },
(k, l) => {
l.Add(new TimeStampedData<OrderSecurity>(kv.Value, now));
if (l.Count >= 10)
l.RemoveAt(0);
});
}
}
}
}
}
@@ -90,8 +90,7 @@ namespace mbts {
}
var sec = order.security;
var sec_meta = sec.HasValue ? _sec_meta_store.get(sec.Value) : null;
var qty_display = sec_meta?.HasQuantityDisplay ?? false ?
sec_meta.Value.QuantityDisplay :
var qty_display = sec_meta?.QuantityDisplay ??
Security.Defaults.quantity_display_settingsi;
return ToastNotifications.order_completed(order, qty_display);
}
+2 -2
View File
@@ -266,8 +266,8 @@ namespace mbts.spark {
// List of symbol to volsurface mapping.
// Publisher: VolSurfaceBlackboardManager
public static ITag<VolSurfaceUsageListImmutable?> VolSurfaceUsageTag { get; } =
_mk_tag<VolSurfaceUsageListImmutable?>();
public static ITag<VolSurfaceUsageListImmutable> VolSurfaceUsageTag { get; } =
_mk_tag<VolSurfaceUsageListImmutable>();
// The AW2 machinary can compute and publish the theos
// right back locally(vs the server doing that) if configured that way.
@@ -10,8 +10,8 @@
</ResourceDictionary.MergedDictionaries>
<Style x:Key="LogViewer.TextBox" TargetType="TextBox">
<Setter Property="Background" Value="{u:BTSColor UI.TextBox.default_background}" />
<Setter Property="Foreground" Value="{u:BTSColor UI.TextBox.default_foreground}" />
<Setter Property="Background" Value="{StaticResource TextBox.BackgroundBrush}" />
<Setter Property="Foreground" Value="{StaticResource TextBox.ForegroundBrush}" />
<Setter Property="BorderThickness" Value="0" />
</Style>
+1 -1
View File
@@ -1 +1 @@
set(sources api/analytics/ExpirationDriver.proto;api/analytics/TimeWeightedInterpolatedDriver.proto;api/apicore.proto;api/apigateway.proto;api/apioptions.proto;api/core/ApiClientInterop.cpp;api/core/ApiServerInterop.cpp;api/domain/Currency.proto;api/domain/SecurityCriteria.proto;api/domain/SecurityCriteriaClause.proto;api/domain/SecurityExpiration.proto;api/domain/SecurityMetadata.proto;api/domain/SecurityMetadataCollection.proto;api/domain/SecurityMetadataItem.proto;api/domain/SecurityType.proto;api/domain/TickDisplayRule.proto;api/domain/TickDisplaySettings.proto;api/domain/TickFactors.proto;api/domain/Venue.proto;api/io/URL.proto;api/md/MarketDataSubscriptionParams.proto;api/md/binance/BinanceDepthStreamParams.proto;api/md/binance/BinanceMarketDataSubscriptionParams.proto;api/md/deribit/DeribitMarketDataSubscriptionParams.proto;api/md/ftx/FTXDepthStreamParams.proto;api/md/ftx/FTXMarketDataSubscriptionParams.proto;api/md/glbx/GlbxMarketDataSubscriptionParams.proto;api/md/huobi/HuobiMarketDataSubscriptionParams.proto;api/public/ApiServer.cpp;api/public/ClientChannel.cpp;api/public/QuickbusClientChannel.cpp;api/public/ServerChannel.cpp;api/public/ServiceContext.cpp;api/service_helpers/AdminServiceUtils.cpp;api/service_helpers/AlgoServiceUtils.cpp;api/service_helpers/PositionUtilities.cpp;api/services/AdminService.proto;api/services/AlgoDefinitionService.proto;api/services/AlgoService.proto;api/services/AnalyticsService.proto;api/services/ApiUnitTestService.proto;api/services/AuthService.proto;api/services/BasePositionService.proto;api/services/ErrorReportService.proto;api/services/HelloService.proto;api/services/MBOService.proto;api/services/MarginService.proto;api/services/MarketDataAuxiliaryService.proto;api/services/MarketDataRelay.proto;api/services/MarketDataService.proto;api/services/MoneyFileService.proto;api/services/OrderService.proto;api/services/OutOfProcessURLListener.proto;api/services/PersistenceService.proto;api/services/PositionService.proto;api/services/ShellDiagnosticService.proto;api/services/ShellLatencyService.proto;api/services/SyntheticSecurityService.proto;api/test/ApiTests.cpp;api/test/HelloServiceServer.cpp;config/ConfigCheck.cpp;config/ConfigDownloader.cpp;config/ConfigErrorCollector.cpp;config/ConfigLog.cpp;config/ConfigPropertyEvaluator.cpp;config/ConfigValueQueue.cpp;config/ConfigurationException.cpp;config/ConfigurationState.cpp;config/PreprocessInputStream.cpp;config/config.cpp;config/defaults/MarketData.conf;config/defaults/Ports.conf;config/defaults/SecurityMetadataCollection.conf;config/defaults/Symbols.conf;config/defaults/cme_reference_data.conf;config/defaults/edge.conf;config/defaults/jetfix.conf;config/defaults/keycode.conf;config/defaults/logging.conf;config/defaults/orderman_cfg.conf;config/defaults/orders_fills_cleanup.conf;config/defaults/position_cfg.conf;config/defaults/span.conf;config/defaults/spreader.conf;config/defaults/testkeys.conf;config/defaults/threads.conf;config/defaults/toggles.conf;config/extensions/extensions.proto;config/invalid/invalid.proto;config/protos/algo_types.proto;config/protos/cme_reference_data.proto;config/protos/exchange_sim_cfg.proto;config/protos/jetfix.proto;config/protos/keycode.proto;config/protos/logging.proto;config/protos/market_data.proto;config/protos/mercury_cfg.proto;config/protos/orderman_cfg.proto;config/protos/orders_fills_cleanup.proto;config/protos/perflog_cfg.proto;config/protos/ports.proto;config/protos/position_cfg.proto;config/protos/quickbus_cfg.proto;config/protos/span.proto;config/protos/spartan.proto;config/protos/spreader.proto;config/protos/subprocess.proto;config/protos/symbols.proto;config/protos/system.proto;config/protos/testkeys.proto;config/protos/threads.proto;config/protos/toggles.proto;config/protos/ui.proto;config/tests/HTTPServer.cpp;config/tests/config_ref_tests.cpp;config/tests/config_tests.cpp;config/text_format/text_format.cpp;domain_model/Security.cpp;domain_model/Security.proto;domain_model/SecurityTests.cpp;domain_model/sod.cpp;domain_model/tests/domain_tests.cpp;expression/BooleanGrammar.cpp;expression/ExpressionEvaluator.cpp;expression/ParserExceptions.cpp;expression/Value.cpp;expression/tests/expression_parser_tests.cpp;io/QueryParams.cpp;io/URL.cpp;io/http/CurlHandle.cpp;io/http/HTTPClient.cpp;io/http/HTTPHeaders.cpp;io/http/HTTPRequest.cpp;io/http/HTTPResponse.cpp;io/websocket/WebsocketClient.cpp;io/websocket/WebsocketClientConfiguration.cpp;io/websocket/WebsocketError.cpp;io/websocket/WebsocketMessage.cpp;io/websocket/WebsocketSSLClientInternal.cpp;io/websocket/tests/WebsocketClientTests.cpp;logging/LoggerHub.cpp;logging/Sink.cpp;logging/logging.cpp;logging/tests/logging_tests.cpp;md/CCApiDepthWalker.cpp;md/CCApiListenerAdaptor.cpp;md/CCApiRequestor.cpp;md/Diag/MDPeriodicDumper.cpp;md/Diag/ShmDumper.cpp;md/MBOProcessor.cpp;md/MBOServiceServer.cpp;md/MDClient.cpp;md/MDConsumer.cpp;md/MDConsumerHandle.cpp;md/MDConsumerInterop.cpp;md/MDProducer.cpp;md/MDProducerHandle.cpp;md/MarketDataLine truncated
set(sources api/analytics/ExpirationDriver.proto;api/analytics/TimeWeightedInterpolatedDriver.proto;api/apicore.proto;api/apigateway.proto;api/apioptions.proto;api/core/ApiClientInterop.cpp;api/core/ApiServerInterop.cpp;api/domain/Currency.proto;api/domain/SecurityCriteria.proto;api/domain/SecurityCriteriaClause.proto;api/domain/SecurityExpiration.proto;api/domain/SecurityMetadata.proto;api/domain/SecurityMetadataCollection.proto;api/domain/SecurityMetadataItem.proto;api/domain/SecurityType.proto;api/domain/TickDisplaySettings.proto;api/domain/TickFactors.proto;api/domain/Venue.proto;api/io/URL.proto;api/md/MarketDataSubscriptionParams.proto;api/md/binance/BinanceDepthStreamParams.proto;api/md/binance/BinanceMarketDataSubscriptionParams.proto;api/md/deribit/DeribitMarketDataSubscriptionParams.proto;api/md/ftx/FTXDepthStreamParams.proto;api/md/ftx/FTXMarketDataSubscriptionParams.proto;api/md/glbx/GlbxMarketDataSubscriptionParams.proto;api/md/huobi/HuobiMarketDataSubscriptionParams.proto;api/public/ApiServer.cpp;api/public/ClientChannel.cpp;api/public/QuickbusClientChannel.cpp;api/public/ServerChannel.cpp;api/public/ServiceContext.cpp;api/service_helpers/AdminServiceUtils.cpp;api/service_helpers/AlgoServiceUtils.cpp;api/service_helpers/PositionUtilities.cpp;api/services/AdminService.proto;api/services/AlgoDefinitionService.proto;api/services/AlgoService.proto;api/services/AnalyticsService.proto;api/services/ApiUnitTestService.proto;api/services/AuthService.proto;api/services/BasePositionService.proto;api/services/ErrorReportService.proto;api/services/HelloService.proto;api/services/MBOService.proto;api/services/MarginService.proto;api/services/MarketDataAuxiliaryService.proto;api/services/MarketDataRelay.proto;api/services/MarketDataService.proto;api/services/MoneyFileService.proto;api/services/OrderService.proto;api/services/OutOfProcessURLListener.proto;api/services/PersistenceService.proto;api/services/PositionService.proto;api/services/ShellDiagnosticService.proto;api/services/ShellLatencyService.proto;api/services/SyntheticSecurityService.proto;api/test/ApiTests.cpp;api/test/HelloServiceServer.cpp;config/ConfigCheck.cpp;config/ConfigDownloader.cpp;config/ConfigErrorCollector.cpp;config/ConfigLog.cpp;config/ConfigPropertyEvaluator.cpp;config/ConfigValueQueue.cpp;config/ConfigurationException.cpp;config/ConfigurationState.cpp;config/PreprocessInputStream.cpp;config/config.cpp;config/defaults/MarketData.conf;config/defaults/Ports.conf;config/defaults/SecurityMetadataCollection.conf;config/defaults/Symbols.conf;config/defaults/cme_reference_data.conf;config/defaults/edge.conf;config/defaults/jetfix.conf;config/defaults/keycode.conf;config/defaults/logging.conf;config/defaults/orderman_cfg.conf;config/defaults/orders_fills_cleanup.conf;config/defaults/position_cfg.conf;config/defaults/span.conf;config/defaults/spreader.conf;config/defaults/testkeys.conf;config/defaults/threads.conf;config/defaults/toggles.conf;config/extensions/extensions.proto;config/invalid/invalid.proto;config/protos/algo_types.proto;config/protos/cme_reference_data.proto;config/protos/exchange_sim_cfg.proto;config/protos/jetfix.proto;config/protos/keycode.proto;config/protos/logging.proto;config/protos/market_data.proto;config/protos/mercury_cfg.proto;config/protos/orderman_cfg.proto;config/protos/orders_fills_cleanup.proto;config/protos/perflog_cfg.proto;config/protos/ports.proto;config/protos/position_cfg.proto;config/protos/quickbus_cfg.proto;config/protos/span.proto;config/protos/spartan.proto;config/protos/spreader.proto;config/protos/subprocess.proto;config/protos/symbols.proto;config/protos/system.proto;config/protos/testkeys.proto;config/protos/threads.proto;config/protos/toggles.proto;config/protos/ui.proto;config/tests/HTTPServer.cpp;config/tests/config_ref_tests.cpp;config/tests/config_tests.cpp;config/text_format/text_format.cpp;domain_model/Security.cpp;domain_model/Security.proto;domain_model/SecurityTests.cpp;domain_model/sod.cpp;domain_model/tests/domain_tests.cpp;expression/BooleanGrammar.cpp;expression/ExpressionEvaluator.cpp;expression/ParserExceptions.cpp;expression/Value.cpp;expression/tests/expression_parser_tests.cpp;io/QueryParams.cpp;io/URL.cpp;io/http/CurlHandle.cpp;io/http/HTTPClient.cpp;io/http/HTTPHeaders.cpp;io/http/HTTPRequest.cpp;io/http/HTTPResponse.cpp;io/websocket/WebsocketClient.cpp;io/websocket/WebsocketClientConfiguration.cpp;io/websocket/WebsocketError.cpp;io/websocket/WebsocketMessage.cpp;io/websocket/WebsocketSSLClientInternal.cpp;io/websocket/tests/WebsocketClientTests.cpp;logging/LoggerHub.cpp;logging/Sink.cpp;logging/logging.cpp;logging/tests/logging_tests.cpp;md/CCApiDepthWalker.cpp;md/CCApiListenerAdaptor.cpp;md/CCApiRequestor.cpp;md/Diag/MDPeriodicDumper.cpp;md/Diag/ShmDumper.cpp;md/MBOProcessor.cpp;md/MBOServiceServer.cpp;md/MDClient.cpp;md/MDConsumer.cpp;md/MDConsumerHandle.cpp;md/MDConsumerInterop.cpp;md/MDProducer.cpp;md/MDProducerHandle.cpp;md/MarketDataError.cpp;md/MarketDataGateway.cpLine truncated
-1
View File
@@ -44,7 +44,6 @@ message PrecomputedTheoPoint {
double vega = 60;
double theta = 70;
double rho = 80;
double vol = 90;
}
message PrecomputedTheos {
@@ -1,22 +0,0 @@
VolSurface {
value {
default_abs_moves: -600
default_abs_moves: -300
default_abs_moves: -200
default_abs_moves: -125
default_abs_moves: -100
default_abs_moves: -75
default_abs_moves: -50
default_abs_moves: -25
default_abs_moves: 0
default_abs_moves: 25
default_abs_moves: 50
default_abs_moves: 75
default_abs_moves: 100
default_abs_moves: 125
default_abs_moves: 200
default_abs_moves: 300
default_abs_moves: 600
}
}
@@ -1,6 +0,0 @@
syntax = 'proto2';
package bts.config;
message VolSurface {
repeated double default_abs_moves = 1;
}
-4
View File
@@ -114,10 +114,6 @@ inline bool is_same(Price p1, Price p2) {
return std::abs(p1.value() - p2.value()) < market_epsilon();
}
inline bool is_same(double d1, double d2) {
return std::abs(d1 - d2) < market_epsilon();
}
inline bool is_equal_or_better(api::Side side, Price p1, Price p2) {
return is_same(p1, p2) || is_better(side, p1, p2);
}
@@ -25,9 +25,5 @@ class ITheoCalculator {
bts::api::orderman::PrecomputedTheoPoint &result) const = 0;
// gets the driver security for a security with id sec_id
virtual std::optional<uint32_t> get_driver_sec(uint32_t sec_id) const = 0;
// given a vol for an option and the current driver, returns the theo that
// corresponds to that vol.
virtual double get_theo_correspoding_to_vol(
uint32_t sec_id, double driver, double vol) const = 0;
};
} // namespace bts::orderman::analytics
@@ -50,15 +50,12 @@ void interpolate(
lower.driver(), lower.theta(), higher.driver(), higher.theta(), driver);
double rho = lin_interp_exterp(
lower.driver(), lower.rho(), higher.driver(), higher.rho(), driver);
double vol = lin_interp_exterp(
lower.driver(), lower.vol(), higher.driver(), higher.vol(), driver);
result.set_theo(theo);
result.set_delta(delta);
result.set_gamma(gamma);
result.set_vega(vega);
result.set_theta(theta);
result.set_rho(rho);
result.set_vol(vol);
}
struct DriverNeighbors {
@@ -216,24 +213,6 @@ std::optional<uint32_t> TheoCalculator::get_driver_sec(uint32_t sec_id) const {
return me->total_theo_data.get_driver_sec(sec_id);
}
double TheoCalculator::get_theo_correspoding_to_vol(
uint32_t sec_id, double driver, double vol) const {
PrecomputedTheoPoint rv;
if (!get_theo(sec_id, driver, rv)) {
return bts::os::nan();
}
// use the vega to compute the theo
double d_vol = vol - rv.vol();
double d_theo = rv.vega() * d_vol;
double new_theo = rv.theo() + d_theo;
LOG(Gen, Info,
"TheoCalculator::get_theo_correspoding_to_vol sec_id: {} driver: {} "
"vol: {} rv.theo: {} rv.vol: {} rv.vega: {} "
"new_theo: {}",
sec_id, driver, vol, rv.theo(), rv.vol(), rv.vega(), new_theo);
return new_theo;
}
void TheoCalculator::set_data(const PrecomputedTheos &theos) {
LOG(Gen, Info, "TheoCalculator::set_data size: {}", theos.theos_size());
WriteLock write_lock(me->lock);
@@ -23,8 +23,6 @@ class TheoCalculator : public ITheoCalculator {
double driver,
bts::api::orderman::PrecomputedTheoPoint &result) const override;
std::optional<uint32_t> get_driver_sec(uint32_t sec_id) const override;
double get_theo_correspoding_to_vol(
uint32_t sec_id, double driver, double vol) const override;
void set_data(const bts::api::orderman::PrecomputedTheos &theos);
private:
@@ -279,13 +279,5 @@ inline Quantity get_update_quantity(const bts::api::orderman::Update &u) {
return rv;
}
inline void set_update_quantity(
bts::api::orderman::Update &state, Quantity cqty) {
uint32_t int_qty = cqty.int_part();
double fractional_qty = cqty.fractional_part();
state.set_quantity(int_qty);
state.mutable_fractional_quantity()->set_fraction(fractional_qty);
}
} // namespace orderman
} // namespace bts
@@ -12,7 +12,6 @@
#include "lib/orderman/orderman_utils/orderman_utils.h"
#include "lib/orderman/orders/algo/algo_orders/LimitCoverAlgo.h"
#include "lib/orderman/orders/algo/algo_orders/TaskingEyeAlgo.h"
#include "lib/orderman/orders/algo/algo_orders/VolAlgo.h"
#include "lib/orderman/orders/algo/algo_orders/limit_cover/LimitCoverAdapter.h"
#include "lib/orderman/orders/algo/algo_orders/tests/ExecutionTesterAlgo.h"
#include "lib/plugin/SparkServices.h"
@@ -300,9 +299,6 @@ extern "C" void init_execution_tester_plugin(bts::SparkServices &srv) {
extern "C" void init_task_eye_plugin(bts::SparkServices &srv) {
DynamicOrderDescriptor tasking_eye_algo;
tasking_eye_algo.set_dynamic_order_type("Tasking Eye");
// if this is set to true, it waits for the existance of Signal File
*tasking_eye_algo.add_param_desc() = param_bool("Wait For Signal");
*tasking_eye_algo.add_param_desc() = param_string("Signal File");
*tasking_eye_algo.add_param_desc() = param_string("Account");
*tasking_eye_algo.add_param_desc() = param_double("Edge");
*tasking_eye_algo.add_param_desc() = param_double("Shot Size");
@@ -329,19 +325,6 @@ extern "C" void init_task_eye_plugin(bts::SparkServices &srv) {
});
}
extern "C" void init_vol_algo_plugin(bts::SparkServices &srv) {
DynamicOrderDescriptor vol_algo;
vol_algo.set_dynamic_order_type("Vol");
//*vol_algo.add_param_desc() = param_string("Account Override");
*vol_algo.add_param_desc() = param_double("Vol");
*vol_algo.add_param_desc() = param_bool("Cross At Entry?");
*vol_algo.add_param_desc() = param_bool("Requote?");
*vol_algo.add_param_desc() = param_bool("Improve Price?");
*vol_algo.add_param_desc() = param_int32("Distance From Top");
srv.register_dynamic_algo_order(
vol_algo, []() { return std::make_unique<orderman::VolAlgo>(); });
}
extern "C" void init_spark_plugin(bts::SparkServices &srv) {
auto &dyn_algo_config = config::get<config::toggles::DynamicAlgos>();
if (dyn_algo_config.enable_dorman())
@@ -362,7 +345,6 @@ extern "C" void init_spark_plugin(bts::SparkServices &srv) {
init_execution_tester_plugin(srv);
// init_sniper_plugin(srv);
init_task_eye_plugin(srv);
init_vol_algo_plugin(srv);
}
} // namespace dorman
@@ -23,17 +23,10 @@
#include "lib/utils/bts_assert.h"
#include <bits/stdint-uintn.h>
#include <boost/algorithm/string.hpp>
#include <chrono>
#include <elf.h>
#include <filesystem>
#include <google/protobuf/timestamp.pb.h>
#include <google/protobuf/util/time_util.h>
#include <iostream>
#include <optional>
#include <sstream>
#include <string>
#include <thread>
#include <tuple>
#include <unordered_map>
using namespace bts::api::orderman;
@@ -76,11 +69,6 @@ double eps = 0.0000001;
bool eq(double d1, double d2) { return std::abs(d1 - d2) < eps; }
bool greater_then_eq(double d1, double d2) { return eq(d1, d2) || d2 > d1; }
void sleep_log() {
return;
std::this_thread::sleep_for(std::chrono::seconds(5));
}
// Returns the number of shots that are within the Shots Window
size_t n_recent_shots(
const std::vector<TEAShot> &shots, uint64_t window_nanos) {
@@ -112,91 +100,7 @@ bool in_flight_orders(const std::vector<TEAShot> &shots) {
return false;
}
// returns the securities that match the criteria from the 2nd - 8th arg
std::set<SecurityKey> match_securities(
const std::vector<api::Security const *> &secs,
api::Venue venue,
const std::string &symbol,
uint32_t exp_month,
uint32_t exp_day,
uint32_t exp_yr,
std::tuple<double, double> strike_range,
api::PutOrCall cp) {
std::set<SecurityKey> rv = {};
double strike_min = std::get<0>(strike_range);
double strike_max = std::get<1>(strike_range);
for (const auto &s : secs) {
if (s->exchange() == venue && s->has_option() && s->symbol() == symbol) {
const auto &oc = s->option();
if (oc.call_put() == cp && oc.strike() >= strike_min &&
oc.strike() <= strike_max && oc.expiration_year() == exp_yr &&
oc.expiration_month() == exp_month &&
oc.expiration_day() == exp_day)
rv.insert(SecurityKey(s->id()));
}
}
return rv;
}
// Parses the sec expressed as text in the secs string.
// the format is symbol:exp:call/put:strike range, ex
// BTC:08/14/2023:Call:25000-30000
std::set<SecurityKey> parse_securities_from_range(
const std::string &secs,
const std::vector<api::Security const *> &sec_vec) {
LOG(OrderMan, Info,
"TaskingEyeAlgo::parse_securities_from_range secs: {} sec_vec: {}", secs,
sec_vec.size());
sleep_log();
if (secs.empty())
return {};
auto parse_exp = [](const std::string &exp_str) {
LOG(OrderMan, Info,
"TaskingEyeAlgo::parse_securities_from_range parse_exp exp: {}",
exp_str);
std::vector<std::string> exp_parts;
boost::split(exp_parts, exp_str, boost::is_any_of("/"));
LOG(OrderMan, Info,
"TaskingEyeAlgo::parse_securities_from_range exp_parts: {}",
exp_parts.size());
uint32_t mm = static_cast<std::uint32_t>(std::stoul(exp_parts[0]));
uint32_t dd = static_cast<std::uint32_t>(std::stoul(exp_parts[1]));
uint32_t yy = static_cast<std::uint32_t>(std::stoul(exp_parts[2]));
LOG(OrderMan, Info,
"TaskingEyeAlgo::parse_securities_from_range {} mm: {} dd: {} yy: {}",
mm, dd, yy);
return std::tuple<uint32_t, uint32_t, uint32_t>(mm, dd, yy);
};
auto parse_strike_range = [](const std::string &strike_range_str) {
std::vector<std::string> strike_parts;
boost::split(strike_parts, strike_range_str, boost::is_any_of("-"));
double k_min = std::stod(strike_parts[0]);
double k_max = std::stod(strike_parts[1]);
return std::tuple<double, double>(k_min, k_max);
};
std::vector<std::string> parts;
boost::split(parts, secs, boost::is_any_of(":"));
const std::string &symbol = parts[0];
const std::string &exp_str = parts[1];
const std::string &cp_str = parts[2];
const auto &strike_range_str = parts[3];
LOG(OrderMan, Info,
"TaskingEyeAlgo::parse_securities_from_range symbol: {} exp: {} cp: {} "
"stike_range: {}",
symbol, exp_str, cp_str, strike_range_str);
// debugging
sleep_log();
auto [mm, dd, yy] = parse_exp(exp_str);
auto strike_range = parse_strike_range(strike_range_str);
api::PutOrCall cp = (cp_str[0] == 'C' || cp_str[0] == 'c')
? api::PutOrCall::PUTORCALL_CALL
: api::PutOrCall::PUTORCALL_PUT;
return match_securities(
sec_vec, api::Venue::VENUE_DERIBIT, symbol, mm, dd, yy, strike_range,
cp);
}
std::set<SecurityKey> parse_securities_from_keys(const std::string &secs) {
std::set<SecurityKey> parse_securities(const std::string &secs) {
std::set<SecurityKey> rv = {};
std::vector<std::string> parts;
boost::split(parts, secs, boost::is_any_of(","));
@@ -209,34 +113,10 @@ std::set<SecurityKey> parse_securities_from_keys(const std::string &secs) {
return rv;
}
std::set<SecurityKey> parse_securities(
const std::string &secs,
const std::vector<api::Security const *> &sec_vec) {
bts_assert(!secs.empty());
LOG(OrderMan, Info, "TaskingEyeAlgo::parse_securities sec: {}", secs);
sleep_log();
std::set<SecurityKey> rv = {};
if (secs.find("/") != std::string::npos &&
secs.find(":") != std::string::npos) {
rv = parse_securities_from_range(secs, sec_vec);
} else {
rv = parse_securities_from_keys(secs);
}
std::stringstream ss;
for (auto k : rv) {
ss << k << " ";
}
LOG(OrderMan, Info, "TaskingEyeAlgo::parse_securities sec: {} rv: {}", secs,
ss.str());
return rv;
}
struct TaskingEyeAlgo ::PImpl {
IAlgoServices *service_ptr = nullptr;
analytics::ITheoCalculator const *theo_calc = nullptr;
TEAState state = TEAState::ACTIVE;
bool wait_for_signal = false;
std::string signal_file = "";
double edge = 100000;
double shot_size = 0;
double target_qty = 0;
@@ -270,62 +150,31 @@ TaskingEyeAlgo::~TaskingEyeAlgo() {}
OrderOpResult TaskingEyeAlgo::do_before_launch(
IBeforeLaunchAlgoServices &serv) {
auto is_data_valid = [&]() {
bool rv = greater_then_eq(0.1, me->shot_size) &&
greater_then_eq(1, me->target_qty) &&
greater_then_eq(1, me->max_shots_per_window) &&
greater_then_eq(1000000000, me->window_size_nanos) &&
me->side != api::SIDE_UNDEFINED;
bool signal_correct = !me->wait_for_signal || !me->signal_file.empty();
rv = rv && signal_correct;
return rv;
return greater_then_eq(0.1, me->shot_size) &&
greater_then_eq(1, me->target_qty) &&
greater_then_eq(1, me->max_shots_per_window) &&
greater_then_eq(1000000000, me->window_size_nanos) &&
me->side != api::SIDE_UNDEFINED;
};
LOG(OrderMan, Info,
"TaskingEyeAlgo::do_before_launch order_key: {} security: {} "
"definition: {}",
serv.order_key().value(), utils::pb::to_string(serv.security()),
utils::pb::to_string(serv.definition()));
sleep_log();
auto &def = serv.definition();
if (def.has_custom_def() && def.custom_def().has_dynamic()) {
const auto &d_params = def.custom_def().dynamic().order_params();
auto wait_for_signal = get_if<bool>(d_params, "Wait For Signal");
me->wait_for_signal =
wait_for_signal.has_value() ? wait_for_signal.value() : false;
auto signal_file = get_if<std::string>(d_params, "Signal File");
me->signal_file = signal_file.has_value() ? signal_file.value() : "";
me->edge = get_if<double>(d_params, "Edge").value();
LOG(OrderMan, Info, "TaskingEyeAlgo::do_before_launch edge: {}",
me->edge);
sleep_log();
me->shot_size = get_if<double>(d_params, "Shot Size").value();
LOG(OrderMan, Info, "TaskingEyeAlgo::do_before_launch shot_size: {}",
me->shot_size);
sleep_log();
me->target_qty = get_if<double>(d_params, "Target Quantity").value();
LOG(OrderMan, Info, "TaskingEyeAlgo::do_before_launch target_qty: {}",
me->target_qty);
sleep_log();
// TODO, make sure the side if right
me->side = def.side();
me->max_shots_per_window =
get_if<uint32_t>(d_params, "Max Shots Per Window").value();
LOG(OrderMan, Info,
"TaskingEyeAlgo::do_before_launch max_shots_per_window: {}",
me->max_shots_per_window);
sleep_log();
uint32_t window_size_secs =
get_if<uint32_t>(d_params, "Window Size (secs)").value();
LOG(OrderMan, Info,
"TaskingEyeAlgo::do_before_launch window_size_secs: {}",
window_size_secs);
sleep_log();
me->window_size_nanos = seconds_to_nanos(window_size_secs);
auto sec_str = get_if<std::string>(d_params, "Securities").value();
LOG(OrderMan, Info, "TaskingEyeAlgo::do_before_launch Securities: {}",
sec_str);
sleep_log();
auto parsed_secs = parse_securities(sec_str, serv.known_securities());
std::stringstream ss;
auto parsed_secs = parse_securities(sec_str);
std::vector<SecurityKey> final_secs;
if (parsed_secs.empty()) {
final_secs.push_back(SecurityKey{serv.security().id()});
@@ -337,21 +186,18 @@ OrderOpResult TaskingEyeAlgo::do_before_launch(
auto ct = get_tea_child_template(serv, sec_key, me->side);
me->sec_map.insert({sec_key, {ct}});
LOG(OrderMan, Info,
"TaskingEyeAlgo::do_before_launch order_key: {} "
"successfully "
"TaskingEyeAlgo::do_before_launch order_key: {} successfully "
"created ChildTemplate for sec_key: {}",
serv.order_key(), sec_key);
}
LOG(OrderMan, Info,
"TaskingEyeAlgo::do_before_launch algo constructed order_key: "
"{} "
"edge: {} shot_size: {} side: {} target_qty: {} "
"max_shots_per_window: {} window_size_nanos: {} n_secs: {} "
"wait_for_signal: {} signal_file: {}",
"TaskingEyeAlgo::do_before_launch algo constructed order_key: {} "
"edge: {} shot_size: {} is_buy: {} target_qty: {} "
"max_shots_per_window: {} window_size_nanos: {} n_secs: {}",
serv.order_key(), me->edge, me->shot_size,
bts::api::Side_Name(me->side), me->target_qty,
me->side == api::SIDE_BUY, me->target_qty,
me->max_shots_per_window, me->window_size_nanos,
me->sec_map.size(), me->wait_for_signal, me->signal_file);
me->sec_map.size());
return {};
}
}
@@ -367,8 +213,7 @@ void TaskingEyeAlgo::do_launch(IAlgoServices &serv) {
for (const auto &kv : me->sec_map) {
auto sec_key = kv.first;
LOG(OrderMan, Info,
"TaskingEyeAlgo::do_before_launch subscribing book for sec_key: "
"{}",
"TaskingEyeAlgo::do_before_launch subscribing book for sec_key: {}",
sec_key);
me->service_ptr->subscribe_book(sec_key);
}
@@ -470,9 +315,9 @@ void TaskingEyeAlgo::on_theos(
if (!sec_data.driver_sec_key.has_value() ||
sec_data.driver_sec_key->value() != driver.value()) {
LOG(OrderMan, Info,
"TaskingEyeAlgo::on_theos order_key: {} subscribing to driver for "
"sec_key: {} driver_key: {}",
me->service_ptr->order_key(), sec_key, driver.value());
"TaskingEyeAlgo::on_theos order_key: {} subscribing to driver "
"sec_key: {}",
me->service_ptr->order_key(), driver.value());
sec_data.driver_sec_key = SecurityKey(driver.value());
sub_to_driver(SecurityKey(driver.value()));
}
@@ -486,14 +331,8 @@ void TaskingEyeAlgo::on_position(PositionUpdate const &pu) {
}
void TaskingEyeAlgo::_evaluate_and_shoot() {
if (me->state != TEAState::ACTIVE) {
LOG(OrderMan, Warn,
"TaskingEyeAlgo::_evaluate_and_shoot order_key: {} state is not "
"active state: {} ",
me->service_ptr->order_key(), me->state);
if (me->state != TEAState::ACTIVE)
return;
}
auto max_shots_per_win_reached = [&]() {
return n_recent_shots(me->shots, me->window_size_nanos) >=
me->max_shots_per_window;
@@ -504,36 +343,12 @@ void TaskingEyeAlgo::_evaluate_and_shoot() {
return true;
return false;
};
auto signal_condition_met = [&]() {
if (me->wait_for_signal) {
return std::filesystem::exists(me->signal_file);
} else
return true;
};
if (!signal_condition_met()) {
// Log ?
LOG(OrderMan, Warn,
"TaskingEyeAlgo::_evaluate_and_shoot order_key: {} Waiting for "
"Signal",
me->service_ptr->order_key());
return;
}
for (auto &kv : me->sec_map) {
auto sec_key = kv.first;
if (max_shots_per_win_reached()) {
LOG(OrderMan, Warn,
"TaskingEyeAlgo::_evaluate_and_shoot order_key: {} "
"max_shots_per_win_reached",
me->service_ptr->order_key());
if (max_shots_per_win_reached())
return;
}
if (sec_has_active_order(sec_key)) {
LOG(OrderMan, Warn,
"TaskingEyeAlgo::_evaluate_and_shoot sec_key: {} has working "
"order, skipping",
sec_key.value());
if (sec_has_active_order(sec_key))
continue;
}
Price price;
Quantity qty;
if (_in_shooting_zone(sec_key, price, qty)) {
@@ -542,8 +357,7 @@ void TaskingEyeAlgo::_evaluate_and_shoot() {
auto ch_ord_key =
me->service_ptr->launch_child(*sec_data.order, price, qty);
LOG(OrderMan, Info,
"TakingEyeAlgo::_evaluate_and_shoot ord_key: {} child_ord_key: "
"{} "
"TakingEyeAlgo::_evaluate_and_shoot ord_key: {} child_ord_key: {} "
"price: {} qty: {}",
me->service_ptr->order_key(), ch_ord_key, price, qty.full_value());
me->shots.push_back(TEAShot{sec_key, ch_ord_key});
@@ -561,13 +375,8 @@ bool TaskingEyeAlgo::_in_shooting_zone(
TEASecData const &sec_data = it->second;
const md::PriceSize &driver_bid = sec_data.driver_book.bid().at(0);
const md::PriceSize &driver_ask = sec_data.driver_book.ask().at(0);
if (eq(driver_bid.size(), 0) || eq(driver_ask.size(), 0)) {
LOG(OrderMan, Warn,
"TaskingEyeAlgo::_in_shooting_zone no driver for sec_key: {} "
"driver_bid_qty: {} driver_ask_qty: {}",
sec_key, driver_bid.size(), driver_ask.size());
if (eq(driver_bid.size(), 0) || eq(driver_ask.size(), 0))
return false;
}
// finding weighted mean (TODO change it to mark price later)
double driver = (driver_bid.price() * driver_bid.size() +
driver_ask.price() * driver_ask.size()) /
@@ -577,55 +386,31 @@ bool TaskingEyeAlgo::_in_shooting_zone(
// TODO normalize theo
double theo = ptp.theo();
theo = theo / driver;
// auto analyze_buy =
auto analyze_buy = [&](const md::PriceSize &md_ask, double theo,
size_t level, Price &price, Quantity &qty) -> bool {
if (me->side == api::SIDE_BUY) {
double ee_buy_pr = theo - me->edge;
bool rv = false;
const md::PriceSize &md_ask = sec_data.sec_book.ask().at(0);
LOG(OrderMan, Diag,
"TaskingEyeAlgo::_in_shooting_zone sec_key: {} driver: {} theo: {} "
"ee_price: {} md: {}/{}",
sec_key, driver, theo, ee_buy_pr, md_ask.price(), md_ask.size());
if (greater_then_eq(1, md_ask.size()) &&
greater_then_eq(md_ask.price(), ee_buy_pr)) {
price = Price(md_ask.price());
qty = me->shot_size;
rv = true;
qty = std::min(static_cast<double>(md_ask.size()), me->shot_size);
return true;
}
LOG(OrderMan, Diag,
"TaskingEyeAlgo::_in_shooting_zone::analyze_buy sec_key: {} "
"theo: {} level: {}"
"ee_buy_pr: {} rv: {}",
sec_key, level, theo, ee_buy_pr, rv);
return rv;
};
auto analyze_sell = [&](const md::PriceSize &md_bid, double theo,
size_t level, Price &price, Quantity &qty) -> bool {
} else {
double ee_sell_pr = theo + me->edge;
bool rv = false;
const md::PriceSize &md_bid = sec_data.sec_book.bid().at(0);
LOG(OrderMan, Diag,
"TaskingEyeAlgo::_in_shooting_zone sec_key: {} driver: {} theo: {} "
"ee_price: {} md: {}/{}",
sec_key, driver, theo, ee_sell_pr, md_bid.price(), md_bid.size());
if (greater_then_eq(1, md_bid.size()) &&
greater_then_eq(ee_sell_pr, md_bid.price())) {
price = Price(md_bid.price());
qty = me->shot_size;
rv = true;
}
LOG(OrderMan, Diag,
"TaskingEyeAlgo::_in_shooting_zone::analyze_sell sec_key: {} theo:"
"{} level: {} ee_sell_pr: {} rv: {}",
sec_key, theo, level, ee_sell_pr, rv);
return rv;
};
if (me->side == api::SIDE_BUY) {
const auto &ask_book = sec_data.sec_book.ask();
for (int i = ask_book.size() - 1; i >= 0; i--) {
const auto &md_ask = ask_book.at(i);
if (analyze_buy(md_ask, theo, i, price, qty)) {
return true;
}
}
} else {
const auto &bid_book = sec_data.sec_book.bid();
for (int i = bid_book.size() - 1; i >= 0; i--) {
const md::PriceSize &md_bid = bid_book.at(i);
if (analyze_sell(md_bid, theo, i, price, qty)) {
return true;
}
qty = std::min(static_cast<double>(md_bid.size()), me->shot_size);
return true;
}
}
return false;
@@ -1,354 +0,0 @@
#include "VolAlgo.h"
#include "lib/api/services/OrderService.pb.h"
#include "lib/domain_model/Security.h"
#include "lib/domain_model/Security.pb.h"
#include "lib/domain_model/SecurityKey.h"
#include "lib/domain_model/common.h"
#include "lib/logging/logging.h"
#include "lib/md/PriceSize.h"
#include "lib/orderman/ChildOrder.h"
#include "lib/orderman/OrderBuilder.h"
#include "lib/orderman/limiter/SecurityLimitNode.h"
#include "lib/orderman/order_factory/IOrderFactory.h"
#include "lib/orderman/orderman_utils/Book.h"
#include "lib/orderman/orderman_utils/orderman_common.h"
#include "lib/orderman/orderman_utils/orderman_utils.h"
#include "lib/orderman/orders/OrderOpResult.h"
#include "lib/orderman/orders/algo/ChildTemplate.h"
#include "lib/orderman/orders/algo/IAlgoOrder.h"
#include "lib/orderman/orders/algo/IAlgoServices.h"
#include "lib/orderman/orders/algo/IBeforeLaunchAlgoServices.h"
#include "lib/os/os.h"
#include "lib/utils/PBUtils.h"
#include "lib/utils/bts_assert.h"
#include <bits/stdint-uintn.h>
#include <google/protobuf/util/time_util.h>
#include <optional>
#include <string>
using namespace bts::api::orderman;
namespace bts::orderman {
ChildTemplate *get_vol_algo_child_template(
IBeforeLaunchAlgoServices &serv,
SecurityKey key,
api::Side side,
std::optional<std::string> account_override) {
OrderDefinition order_def = serv.definition();
order_def.set_security_key(key.value());
order_def.set_side(side);
order_def.set_time_in_force(order_def.time_in_force());
if (account_override.has_value()) {
// no need for it now
}
order_def.mutable_custom_def()->mutable_limit()->MergeFrom(
api::orderman::LimitOrderDefinition());
return &(serv.make_child_template(order_def));
}
struct VolAlgoParams {
std::optional<std::string> account_override;
double vol_perc;
bool cross_at_entry = false;
bool requote = false;
bool improve_price = false;
int distance_from_top = 2;
};
std::optional<VolAlgoParams> parse_parameters(
const DynamicOrderDefinition &def) {
const auto &d_params = def.order_params();
VolAlgoParams rv = {};
// vol
auto vol_perc = get_if<double>(d_params, "Vol");
if (!vol_perc.has_value())
return std::nullopt;
rv.vol_perc = vol_perc.value();
// cross at entry
auto cross_at_entry = get_if<bool>(d_params, "Cross At Entry?");
rv.cross_at_entry =
cross_at_entry.has_value() ? cross_at_entry.value() : false;
// requote
auto requote = get_if<bool>(d_params, "Requote?");
rv.requote = requote.has_value() ? requote.value() : false;
// improve price
auto improve_price = get_if<bool>(d_params, "Improve Price?");
rv.improve_price = improve_price.has_value() ? improve_price.value() : false;
// Distance from Top
auto distance_from_top = get_if<int>(d_params, "Distance From Top");
if (!distance_from_top.has_value())
return std::nullopt;
rv.distance_from_top = distance_from_top.value();
return rv;
}
struct VolAlgo ::PImpl {
IAlgoServices *service_ptr = nullptr;
analytics::ITheoCalculator const *theo_calc = nullptr;
OrderKey order_key;
uint32_t sec_key;
api::Side side;
std::optional<std::string> account_override;
double qty = 0;
VolAlgoParams params = {};
uint32_t driver_sec_key = 0;
bool subbed_to_driver = false;
std::optional<Book> driver_book = std::nullopt;
std::optional<Book> sec_book = std::nullopt;
// out
ChildTemplate *child_order = nullptr;
std::optional<OrderKey> child_order_key = std::nullopt;
Price current_price = Price{os::nan()};
bool inited() const {
// once we have receved both books we are ready to go
return driver_book.has_value() && sec_book.has_value();
}
bool order_fired() const { return child_order_key.has_value(); }
};
VolAlgo::VolAlgo() : me(new PImpl()) { LOG(OrderMan, Info, "VolAlgo created"); }
VolAlgo::~VolAlgo() {}
OrderOpResult VolAlgo::do_before_launch(IBeforeLaunchAlgoServices &serv) {
LOG(OrderMan, Info,
"VolAlgo::do_before_launch order_key: {} security: {} "
"definition: {}",
serv.order_key().value(), utils::pb::to_string(serv.security()),
utils::pb::to_string(serv.definition()));
auto &def = serv.definition();
me->order_key = serv.order_key();
me->sec_key = def.security_key();
me->side = def.side();
if (def.has_custom_def() && def.custom_def().has_dynamic()) {
auto va_params = parse_parameters(def.custom_def().dynamic());
if (!va_params.has_value()) {
return {
bts::api::orderman::OrderError::ORDER_ERROR_INVALID_PARAMETER,
std::string("Parameters are not well formed")};
}
me->params = va_params.value();
me->child_order = get_vol_algo_child_template(
serv, SecurityKey(me->sec_key), me->side,
me->params.account_override);
LOG(OrderMan, Info,
"VolAlgo::do_before_launch order_key: {} sec_key: {} side: {} vol: "
"{} cross_at_entry: {} re_quote: {} improve_price: {} "
"distance_from_top: {}",
me->order_key, me->sec_key, bts::api::Side_Name(me->side),
me->params.vol_perc, me->params.cross_at_entry, me->params.requote,
me->params.improve_price, me->params.distance_from_top);
return {};
}
return {
bts::api::orderman::OrderError::ORDER_ERROR_INVALID_PARAMETER,
std::string("Parameters are not well formed")};
}
void VolAlgo::do_launch(IAlgoServices &serv) {
LOG(OrderMan, Info, "VolAlgo::do_launch order_key: {} qty: {}",
serv.order_key().value(), serv.quantity());
me->service_ptr = &serv;
me->qty = serv.quantity().full_value();
me->service_ptr->subscribe_theos();
me->service_ptr->subscribe_timer();
me->service_ptr->subscribe_book(SecurityKey(me->sec_key));
}
void VolAlgo::do_launch_attached(IAlgoServices &serv, OrderKey, ChildState) {
me->service_ptr = &serv;
_terminate_algo(true, "Vol Algo can not be attached to working orders");
}
void VolAlgo::on_book(SecurityKey sec_key, Book const &book) {
LOG(OrderMan, Diag, "VolAlgo::on_book order_key: {} sec_key: {}",
me->order_key, sec_key);
if (sec_key.value() == me->sec_key) {
me->sec_book = book;
_analyze_and_act();
} else if (sec_key.value() == me->driver_sec_key) {
me->driver_book = book;
_analyze_and_act();
}
}
void VolAlgo::do_update(api::orderman::Update const &u) {
LOG(OrderMan, Info,
"VolAlgo::do_update order_key: {} sec_key: {} "
"update: {}",
me->order_key, me->sec_key, utils::pb::to_string(u));
const auto &p = u.custom().dynamic();
auto params = parse_parameters(p);
if (!params.has_value()) {
LOG(Gen, Error,
"VolAlgo::do_update order_key: {} parameters are not well formed",
me->order_key);
return;
}
me->qty = u.quantity();
me->params = params.value();
_analyze_and_act(true);
// price changes are ignored the price is the vol
}
void VolAlgo::on_child_change(OrderKey, ChildState const &state) {
// price / qty is reflected from the child
me->service_ptr->update_fill_state(state.total_fills, state.last_price);
switch (state.status) {
case ChildStatus::WORKING:
break;
case ChildStatus::COMPLETED:
me->service_ptr->complete();
break;
case ChildStatus::CANCELED:
me->service_ptr->cancel("Child Order Cancelled");
break;
}
}
void VolAlgo::on_status(SecurityKey, co2::TradingStatus) {}
void VolAlgo::on_timer() {}
void VolAlgo::on_theos(analytics::ITheoCalculator const *theo_calculator) {
LOG(OrderMan, Info, "VolAlgo::on_theos order_key: {}", me->order_key);
me->theo_calc = theo_calculator;
if (me->subbed_to_driver) {
_analyze_and_act();
return;
}
auto driver_sec = me->theo_calc->get_driver_sec(me->sec_key);
if (driver_sec.has_value()) {
LOG(OrderMan, Info,
"VolAlgo::on_theos order_key: {} subscribing to driver sec_key: {}",
me->order_key, driver_sec.value());
me->subbed_to_driver = true;
me->driver_sec_key = driver_sec.value();
me->service_ptr->subscribe_book(SecurityKey(me->driver_sec_key));
} else {
LOG(OrderMan, Warn,
"VolAlgo::on_theos order_key: {} could not find driver",
me->order_key);
}
}
void VolAlgo::on_position(PositionUpdate const &) {}
// this is where all the action happens, both new order (just once) as well as
// order updates
void VolAlgo::_analyze_and_act(bool force_reprice) {
// check for period (to stop frequent updates)
auto get_driver = [&]() {
const md::PriceSize &driver_bid = me->driver_book->bid().at(0);
const md::PriceSize &driver_ask = me->driver_book->ask().at(0);
if (bts::securities::is_same(driver_bid.size(), 0) ||
bts::securities::is_same(driver_ask.size(), 0))
return os::nan();
// finding weighted mean (TODO change it to mark price later)
double driver = (driver_bid.price() * driver_bid.size() +
driver_ask.price() * driver_ask.size()) /
(driver_bid.size() + driver_ask.size());
return driver;
};
auto crosses_opp_market = [&](Price order_price) {
if (me->side == api::SIDE_BUY) {
double top = me->sec_book->ask().at(0).price();
return securities::gr_than_eq(order_price.value(), top);
} else {
double top = me->sec_book->bid().at(0).price();
return securities::gr_than_eq(top, order_price.value());
}
};
if (me->order_fired()) {
// repricing if parameters (and arg) allows
if (!me->params.requote && !force_reprice)
return;
// working order
double driver = get_driver();
if (os::is_nan(driver)) {
// keep working it
return;
}
double theo_unnorm = me->theo_calc->get_theo_correspoding_to_vol(
me->sec_key, driver, me->params.vol_perc / 100.0);
// TODO: normalize
double theo = theo_unnorm / driver;
Price new_price = _get_order_price(theo);
LOG(OrderMan, Info,
"VolAlgo::_analyze_and_act (calc updating) order_key: {} "
"theo_unnorm: {} driver: {} theo: {} order_price: {}",
me->order_key, theo_unnorm, driver, theo, new_price);
if (securities::is_same(me->current_price, new_price)) {
return;
}
bool improving =
securities::is_better(me->side, new_price, me->current_price);
if (!improving || me->params.improve_price || force_reprice) {
bts::api::orderman::Update update;
update.set_price(new_price.value());
set_update_quantity(update, Quantity::create_new(me->qty));
me->current_price = new_price;
LOG(Gen, Info,
"VolAlgo::_analyze_and_act updating order order_key: {} price: {} "
"qty: {}",
me->order_key, me->current_price, me->qty);
me->service_ptr->update_child(me->child_order_key.value(), update);
return;
}
} else {
// new order path
if (me->inited()) {
// time to fire
double driver = get_driver();
if (os::is_nan(driver)) {
return;
}
// entering order
double theo_unnorm = me->theo_calc->get_theo_correspoding_to_vol(
me->sec_key, driver, me->params.vol_perc / 100.0);
double theo = theo_unnorm / driver;
Price order_price = _get_order_price(theo);
LOG(OrderMan, Info,
"VolAlgo::_analyze_and_act (calc new) order_key: {} theo_unnorm: "
"{} driver: {} theo: {} order_price: {}",
me->order_key, theo_unnorm, driver, theo, order_price);
if (!me->params.cross_at_entry && crosses_opp_market(order_price))
return;
me->current_price = order_price;
LOG(Gen, Info,
"VolAlgo::_analyze_and_act placing order order_key: {} price: {} "
"qty: {}",
me->order_key, me->current_price, me->qty);
me->child_order_key = me->service_ptr->launch_child(
*me->child_order, me->current_price,
Quantity::create_new(me->qty));
}
}
}
void VolAlgo::_terminate_algo(bool is_error, const std::string &err_msg) {
// TODO unsub ?
if (is_error) {
me->service_ptr->terminate_error(err_msg);
} else {
me->service_ptr->complete();
}
}
Price VolAlgo::_get_order_price(double theo) const {
// BTC Opts (p <= 0.005) : price = 0.0001 qty = 0.1
// BTC Opts (p > 0.005) : price = 0.0005 qty = 0.1
// -------------------------------------------------
// ETH Opts (p <= 0.005) : price = 0.0001 qty = 1
// ETH Opts (p > 0.005) : price = 0.0005 qty = 1
// TODO: get the above from the security itself
double rv = 0;
if (theo > 0.005) {
rv = securities::raw_round_to_quote_price(theo, 0.0005, me->side);
} else {
rv = securities::raw_round_to_quote_price(theo, 0.0001, me->side);
}
LOG(Gen, Info,
"VolAlgo::_get_order_price order_key: {} theo: {} order_price: {}",
me->order_key, theo, rv);
return Price{rv};
}
} // namespace bts::orderman
@@ -1,38 +0,0 @@
#pragma once
#include "lib/domain_model/SecurityKey.h"
#include "lib/domain_model/common.h"
#include "lib/orderman/calculations/ITheoCalculator.h"
#include "lib/orderman/orders/algo/IAlgoOrder.h"
#include <openssl/ossl_typ.h>
namespace bts::orderman {
// This is a test algo, not to be used in prod, that sends an order and modifies
// it by moving the price up and down at a given period
class VolAlgo : public IAlgoOrder {
public:
VolAlgo();
~VolAlgo();
OrderOpResult do_before_launch(IBeforeLaunchAlgoServices &) override;
void on_book(SecurityKey, Book const &) override;
void do_launch(IAlgoServices &) override;
void do_launch_attached(IAlgoServices &, OrderKey, ChildState) override;
void do_update(api::orderman::Update const &) override;
void on_child_change(OrderKey, ChildState const &) override;
void on_status(SecurityKey, co2::TradingStatus) override;
void on_timer() override;
void on_theos(analytics::ITheoCalculator const *theo_calculator) override;
void on_position(PositionUpdate const &) override;
private:
struct PImpl;
std::unique_ptr<PImpl> me;
private:
void _analyze_and_act(bool force_reprice = false);
void _terminate_algo(bool is_error, const std::string &err_msg);
Price _get_order_price(double theo) const;
};
} // namespace bts::orderman
+2 -3
View File
@@ -8,18 +8,17 @@ if exist %venv_dir% (
echo Removing python virtual environment
rmdir /S /Q %venv_dir%
) else (
GOTO END
GOTO ERROR
)
)
echo Creating python virtual environment
@call python -m venv %venv_dir%
@call %venv_dir%\Scripts\activate.bat
@call %venv_dir%\Scripts\python -m pip install --upgrade pip
@call pip install --upgrade pip
@call %venv_dir%\Scripts\pip install -r %~dp0..\requirements.txt
@call deactivate
:END
EXIT /B %ERRORLEVEL%
:ERROR
-13
View File
@@ -1,13 +0,0 @@
@echo off
@REM Build script designed for our jenkins build job(s). It assumes the py
@REM venv already exists.
setlocal
set tools_dir=%~dp0
set venv_dir=%~dp0..\.venv\
call "C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\Tools\VsDevCmd.bat"
call "%venv_dir%Scripts\activate.bat"
call python "%tools_dir%py\build_spark.py" %*
EXIT /B %ERRORLEVEL%
+4 -4
View File
@@ -173,7 +173,8 @@ def _get_arg_parser():
metavar='debug|release',
type=str,
nargs='?',
help='The build configuration')
help='The build configuration',
default='Debug')
parser.add_argument(
'-o',
@@ -335,10 +336,9 @@ def _main():
link_build_dir = arg_to_path(get_arg(args_dict, 'link_output_dir'))
targets = flatten_arglist(get_arg(args_dict, 'target'))
verbosity = get_arg(args_dict, 'verbosity') or None
configuration = args.configuration or 'Release' if targets is not None and any(
filter(lambda t: t.lower() == 'publish', targets)) else 'Debug'
build(
configuration=configuration,
configuration=args.configuration,
clean=args.clean,
restore=args.restore,
build_dir=build_dir,