Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5b1cf890e8 | ||
|
|
c254248886 | ||
|
|
05741ceab0 | ||
|
|
9e3b3879d0 | ||
|
|
a1b189fcb1 | ||
|
|
7a48e5dd5c | ||
|
|
2842aea933 | ||
|
|
f97d156020 | ||
|
|
d8922be0ad | ||
|
|
5634ada367 | ||
|
|
94d249edbd | ||
|
|
1c29ac0ec5 | ||
|
|
e7e280bad8 | ||
|
|
7ca11c3eee | ||
|
|
caf6ac029c | ||
|
|
4c0fa81963 | ||
|
|
720c1595e0 | ||
|
|
bedd0051a9 | ||
|
|
09bed1a713 | ||
|
|
ebb53c5035 | ||
|
|
243b82a6d7 | ||
|
|
bd4f840d27 | ||
|
|
de893d2175 | ||
|
|
33658f13f6 | ||
|
|
469ee033b2 | ||
|
|
451fb67195 | ||
|
|
a21739a866 | ||
|
|
7d694f5c04 | ||
|
|
14e08cf508 | ||
|
|
12349ef8b7 |
No files matched your search
@@ -22,4 +22,5 @@ node_modules
|
||||
paket-files
|
||||
project.lock.json
|
||||
src/server/sparkd/orderman/perf_analysis/data
|
||||
src/server/sparkd/orderman/perf_analysis/logs/
|
||||
src/server/sparkd/orderman/perf_analysis/trace_data/
|
||||
@@ -121,7 +121,7 @@ namespace mbts.spark {
|
||||
|
||||
public Row(TimeAndSalesVM parent, SecurityImmutable security, TradeMessageImmutable trade) : base(security) {
|
||||
_trade_key = trade.Key;
|
||||
time = trade.Time.lldatetime();
|
||||
time = ((ulong)trade.Time).lldatetime().ToLocalTime();
|
||||
price = trade.Mantissa * Math.Pow(10, trade.Exponent);
|
||||
size = trade.Size;
|
||||
aggressor_side = trade.AggressorSide;
|
||||
|
||||
@@ -134,7 +134,9 @@ namespace bts.spark {
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var (neworder, oldorder) in update.changes) {
|
||||
foreach (var order in update.changes) {
|
||||
var neworder = order.New;
|
||||
var oldorder = order.Old;
|
||||
(var security, var spec) = get_security(neworder.Def.SecurityKey);
|
||||
//
|
||||
// description
|
||||
|
||||
@@ -24,13 +24,15 @@ namespace mbts.spark
|
||||
this.child_order_types = new InlineUserProperty<IReadOnlyCollection<CustomOneofCase>>(child_order_types, true);
|
||||
cross_threshold = new InlineUserProperty<bts.spark.QuantityMeasure>(
|
||||
new bts.spark.QuantityMeasure() { Ratio = 1.0, Absolute = 1}, false);
|
||||
max_chase_ticks = new InlineUserProperty<uint>(10, false);
|
||||
max_chase_ticks = new InlineUserProperty<uint>(2, false);
|
||||
trade_toward_behavior = new InlineUserProperty<LeanOrderDefinition.Types.OnToward>(LeanOrderDefinition.Types.OnToward.Cancel, false);
|
||||
}
|
||||
public readonly InlineUserProperty<CustomOneofCase> selected_child_order_type;
|
||||
public readonly InlineUserProperty<IReadOnlyCollection<CustomOneofCase>> child_order_types;
|
||||
|
||||
public readonly InlineUserProperty<bts.spark.QuantityMeasure> cross_threshold;
|
||||
public readonly InlineUserProperty<uint> max_chase_ticks;
|
||||
public readonly InlineUserProperty<LeanOrderDefinition.Types.OnToward> trade_toward_behavior;
|
||||
}
|
||||
|
||||
class LeanOrderVM : OrderTypeVMBase<LeanOrderDefinition>, IHierarchicalOrderTypeVM, IDisposable
|
||||
@@ -44,7 +46,8 @@ namespace mbts.spark
|
||||
IDisposable _dispose, _child_order_errors_sub;
|
||||
|
||||
public bts.spark.QuantityMeasure cross_threshold { get; set; } = new bts.spark.QuantityMeasure() { Ratio = 1.0, Absolute = 1 };
|
||||
public uint max_chase_ticks { get; set; } = 10;
|
||||
public uint max_chase_ticks { get; set; } = 2;
|
||||
public LeanOrderDefinition.Types.OnToward trade_toward_behavior { get; set; } = LeanOrderDefinition.Types.OnToward.Cancel;
|
||||
|
||||
public LeanOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source)
|
||||
: this(model, security_md_source, security_md_source.Select(smd =>
|
||||
@@ -77,6 +80,11 @@ namespace mbts.spark
|
||||
max_chase_ticks = p.max_chase_ticks.value;
|
||||
RaisePropertyChanged(nameof(max_chase_ticks));
|
||||
}
|
||||
if (p.trade_toward_behavior.force_override)
|
||||
{
|
||||
trade_toward_behavior = p.trade_toward_behavior.value;
|
||||
RaisePropertyChanged(nameof(trade_toward_behavior));
|
||||
}
|
||||
_inline_props = p;
|
||||
_validate();
|
||||
})
|
||||
@@ -94,10 +102,13 @@ namespace mbts.spark
|
||||
var i = factory.lean_order();
|
||||
i.CrossThreshold = cross_threshold;
|
||||
i.MaxChaseTicks = max_chase_ticks;
|
||||
i.TradeTowardBehavior = trade_toward_behavior;
|
||||
child_order.assign_to(i.Child);
|
||||
value.Lean = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<LeanOrderDefinition.Types.OnToward> trade_toward_types => EnumHelper.get_values_except<LeanOrderDefinition.Types.OnToward>();
|
||||
|
||||
public ICollection<IOrderTypeVM> child_order_types
|
||||
{
|
||||
|
||||
@@ -222,6 +222,8 @@
|
||||
<RowDefinition Height="4" />
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="4" />
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="4" />
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
@@ -233,9 +235,11 @@
|
||||
<uag:QuantityMeasureControl Grid.Row="1" Grid.Column="2" QuantityMeasure="{Binding cross_threshold}"/>
|
||||
<TextBlock Grid.Row="3" VerticalAlignment="Center">Max Chase Ticks</TextBlock>
|
||||
<c:TickUpDownUInt Grid.Row="3" Grid.Column="2" Value="{Binding max_chase_ticks}"/>
|
||||
<TextBlock Grid.Row="5">Child</TextBlock>
|
||||
<TextBlock Grid.Row="5">On Toward</TextBlock>
|
||||
<ComboBox Grid.Row="5" Grid.Column="2" ItemsSource="{Binding trade_toward_types}" ItemTemplate="{StaticResource EnumDescriptionTemplate}" SelectedItem="{Binding trade_toward_behavior}"/>
|
||||
<TextBlock Grid.Row="7">Child</TextBlock>
|
||||
<uag:OrderDetailsPopupControl
|
||||
Grid.Row="5" Grid.Column="2"
|
||||
Grid.Row="7" Grid.Column="2"
|
||||
IsOpen="{Binding child_order.editable_properties_open, FallbackValue=False}"
|
||||
ItemsSource="{Binding child_order_types}"
|
||||
SelectedItem="{Binding child_order}"
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
using bts.api.core;
|
||||
using bts.api.persistence;
|
||||
using bts.api.user;
|
||||
using bts.test;
|
||||
using mbts.persist;
|
||||
using mbts.ui.shell;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace bts.settings {
|
||||
public class SettingsManagerTool {
|
||||
public static void run() {
|
||||
var sc = new ServiceContext();
|
||||
var users = sc.locate<UserService>();
|
||||
var persist = sc.locate<PersistenceService>("settings");
|
||||
var user_list = users.get_users(new GetUsersRequest());
|
||||
var user_names = user_list.Users.Select(u => u.Login);
|
||||
var from = ConsoleEx.prompt("From User", user_names);
|
||||
var from_persist = new UserUISettings(user_list.Users.First(u => u.Login == from).Id, persist, null);
|
||||
|
||||
var workspaces = from_persist.query<WorkspaceDefinition>(WorkspacesModel.WORKSPACE_NAMESPACE, ".*").Result;
|
||||
var selected_ws = ConsoleEx.prompt("Choose workspace:", workspaces.Select(w => w.key));
|
||||
|
||||
var to = ConsoleEx.prompt("To User", user_names);
|
||||
|
||||
var name = ConsoleEx.input_prompt("Rename workspace?", selected_ws);
|
||||
|
||||
var confirm = ConsoleEx.prompt($"copy workspace '{selected_ws}' from {from} to {to} and name it '{name}'. Confirm?", "Yes", "No");
|
||||
if (confirm == "Yes") {
|
||||
var ws_blob = workspaces.First(w => w.key == selected_ws).value;
|
||||
var to_persist = new UserUISettings(user_list.Users.First(u => u.Login == to).Id, persist, null);
|
||||
to_persist.set(WorkspacesModel.WORKSPACE_NAMESPACE, name, ws_blob).Wait();
|
||||
Console.WriteLine("Done");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
@@ -53,28 +54,43 @@ namespace bts.api.core {
|
||||
#endif
|
||||
}
|
||||
|
||||
public readonly struct Change<T> {
|
||||
public Change(T o, T n) {
|
||||
Old = o;
|
||||
New = n;
|
||||
}
|
||||
public readonly T Old;
|
||||
public readonly T New;
|
||||
public Change<T> merge(Change<T> newer) {
|
||||
return new Change<T>(Old, newer.New);
|
||||
}
|
||||
public override string ToString() {
|
||||
return $"{{{Old}=>{New}}}";
|
||||
}
|
||||
}
|
||||
|
||||
public class Update<T, TKey> where TKey : IEquatable<TKey> {
|
||||
private Func<T, TKey> _key;
|
||||
internal Update(
|
||||
IReadOnlyCollection<T> all,
|
||||
IEnumerable<T> added,
|
||||
IEnumerable<(T New,T Old)> changes,
|
||||
IEnumerable<Change<T>> changes,
|
||||
IEnumerable<T> removed,
|
||||
Func<T, TKey> key_extractor) {
|
||||
this.added = added ?? new T[0];
|
||||
this.removed = removed ?? new T[0];
|
||||
this.changes = changes ?? new (T,T)[0];
|
||||
this.changes = changes ?? new Change<T>[0];
|
||||
this.all = all;
|
||||
_key = key_extractor;
|
||||
}
|
||||
|
||||
public static Update<T, TKey> empty(Func<T, TKey> key_extractor) =>
|
||||
new Update<T, TKey>(new T[0], new T[0], new (T,T)[0], new T[0], key_extractor);
|
||||
new Update<T, TKey>(new T[0], new T[0], new Change<T>[0], new T[0], key_extractor);
|
||||
|
||||
public Update<T,TKey> merge(Update<T, TKey> next) {
|
||||
var comp = new RowEqualityComparer<T, TKey>(_key);
|
||||
var added = this.added as HashSet<T> ?? new HashSet<T>(this.added, comp);
|
||||
var changes = this.changed as HashSet<(T New,T Old)> ?? new HashSet<(T,T)>(this.changes, comp);
|
||||
var changes = this.changed as ChangeDict<T,TKey> ?? new ChangeDict<T,TKey>(this.changes, _key);
|
||||
var removed = this.removed as HashSet<T> ?? new HashSet<T>(this.removed, comp);
|
||||
|
||||
// Add
|
||||
@@ -87,9 +103,7 @@ namespace bts.api.core {
|
||||
added.Add(c.New);
|
||||
}
|
||||
else {
|
||||
// Remove does not error if it was not present
|
||||
changes.Remove(c);
|
||||
changes.Add(c);
|
||||
changes.AddOrMerge(c);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,7 +112,7 @@ namespace bts.api.core {
|
||||
if (!added.Remove(r)) {
|
||||
removed.Add(r);
|
||||
}
|
||||
changes.Remove((r,r));
|
||||
changes.Remove(_key(r));
|
||||
}
|
||||
|
||||
return new Update<T, TKey>(
|
||||
@@ -110,7 +124,7 @@ namespace bts.api.core {
|
||||
|
||||
public IReadOnlyCollection<T> all { get; }
|
||||
public IEnumerable<T> added { get; }
|
||||
public IEnumerable<(T New,T Old)> changes { get; }
|
||||
public IEnumerable<Change<T>> changes { get; }
|
||||
public IEnumerable<T> changed => changes.Select(c => c.New);
|
||||
public IEnumerable<T> removed { get; }
|
||||
|
||||
@@ -127,7 +141,40 @@ namespace bts.api.core {
|
||||
#endif
|
||||
}
|
||||
|
||||
struct RowEqualityComparer<T,TKey> : IEqualityComparer<T>, IEqualityComparer<(T,T)>
|
||||
class ChangeDict<T, TKey> : IEnumerable<Change<T>> {
|
||||
Dictionary<TKey, Change<T>> _changes;
|
||||
Func<Change<T>, TKey> _key;
|
||||
|
||||
public ChangeDict(IEnumerable<Change<T>> changes, Func<T, TKey> key) {
|
||||
_key = x => key(x.New);
|
||||
_changes = changes.ToDictionary(_key);
|
||||
}
|
||||
|
||||
public Change<T>? AddOrMerge(Change<T> newer) {
|
||||
var key = _key(newer);
|
||||
if (_changes.TryGetValue(key, out Change<T> old)) {
|
||||
_changes[key] = old.merge(newer);
|
||||
return old;
|
||||
}
|
||||
_changes.Add(key, newer);
|
||||
return null;
|
||||
}
|
||||
|
||||
public void Remove(TKey key) {
|
||||
_changes.Remove(key);
|
||||
}
|
||||
|
||||
#region IEnumerable
|
||||
public IEnumerator<Change<T>> GetEnumerator() {
|
||||
return _changes.Values.GetEnumerator();
|
||||
}
|
||||
IEnumerator IEnumerable.GetEnumerator() {
|
||||
return _changes.Values.GetEnumerator();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
struct RowEqualityComparer<T,TKey> : IEqualityComparer<T>, IEqualityComparer<Change<T>>
|
||||
where TKey : IEquatable<TKey> {
|
||||
readonly Func<T,TKey> _key;
|
||||
|
||||
@@ -139,16 +186,16 @@ namespace bts.api.core {
|
||||
return _key(x).Equals(_key(y));
|
||||
}
|
||||
|
||||
public bool Equals((T, T) x, (T, T) y) {
|
||||
return Equals(x.Item1, y.Item1);
|
||||
public bool Equals(Change<T> x, Change<T> y) {
|
||||
return Equals(x.New, y.New);
|
||||
}
|
||||
|
||||
public int GetHashCode(T obj) {
|
||||
return _key(obj).GetHashCode();
|
||||
}
|
||||
|
||||
public int GetHashCode((T, T) obj) {
|
||||
return GetHashCode(obj.Item1);
|
||||
public int GetHashCode(Change<T> c) {
|
||||
return GetHashCode(c.New);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,7 +206,7 @@ namespace bts.api.core {
|
||||
internal TableUpdate(
|
||||
ImmutableDictionary<TKey, T> init,
|
||||
IEnumerable<T> added,
|
||||
IEnumerable<(T,T)> changed,
|
||||
IEnumerable<Change<T>> changed,
|
||||
IEnumerable<T> removed) {
|
||||
_state = init;
|
||||
this.added = added;
|
||||
@@ -169,7 +216,7 @@ namespace bts.api.core {
|
||||
|
||||
public IReadOnlyDictionary<TKey, T> state { get { return _state; } }
|
||||
public IEnumerable<T> added { get; }
|
||||
public IEnumerable<(T New,T Old)> changes { get; }
|
||||
public IEnumerable<Change<T>> changes { get; }
|
||||
public IEnumerable<T> changed => changes.Select(c => c.New);
|
||||
|
||||
public IEnumerable<T> removed { get; }
|
||||
@@ -185,7 +232,7 @@ namespace bts.api.core {
|
||||
}
|
||||
|
||||
internal TableUpdate<T, TKey> as_snapshot() {
|
||||
return new TableUpdate<T, TKey>(_state, _state.Values.ToList(), new (T,T)[0], new T[0]);
|
||||
return new TableUpdate<T, TKey>(_state, _state.Values.ToList(), new Change<T>[0], new T[0]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,7 +255,7 @@ namespace bts.api.core {
|
||||
if (raw.is_reset) {
|
||||
var removed = state.Values.ToList();
|
||||
state.Clear();
|
||||
return new Update<T, TKey>(new T[0], new T[0], new (T,T)[0], removed, key_extractor);
|
||||
return new Update<T, TKey>(new T[0], new T[0], new Change<T>[0], removed, key_extractor);
|
||||
}
|
||||
else {
|
||||
var removed = new List<T>(raw.removed_keys.Count);
|
||||
@@ -222,11 +269,11 @@ namespace bts.api.core {
|
||||
for (int x = 0; x < raw.added_items.Count; ++x) {
|
||||
state[raw.added_keys[x]] = raw.added_items[x];
|
||||
}
|
||||
List<(T, T)> changes = new List<(T, T)>(raw.changed_items.Count);
|
||||
var changes = new List<Change<T>>(raw.changed_items.Count);
|
||||
for (int x = 0; x < raw.changed_items.Count; ++x) {
|
||||
var old = state[raw.changed_keys[x]];
|
||||
state[raw.changed_keys[x]] = raw.changed_items[x];
|
||||
changes.Add((raw.changed_items[x], old));
|
||||
changes.Add(new Change<T>(old, raw.changed_items[x]));
|
||||
}
|
||||
return new Update<T, TKey>(state.Values.ToList(), raw.added_items.ToList(), changes, removed, key_extractor);
|
||||
}
|
||||
@@ -235,7 +282,7 @@ namespace bts.api.core {
|
||||
|
||||
public static IObservable<TableUpdate<T,TKey>> Table<T,TKey>(this IObservable<Update<T,TKey>> source) where TKey : IEquatable<TKey> =>
|
||||
source.Scan(
|
||||
new TableUpdate<T,TKey>(ImmutableDictionary<TKey,T>.Empty, new T[0], new (T,T)[0], new T[0]),
|
||||
new TableUpdate<T,TKey>(ImmutableDictionary<TKey,T>.Empty, new T[0], new Change<T>[0], new T[0]),
|
||||
(table, update) => TableUpdate<T,TKey>.update(table,update));
|
||||
|
||||
// Fold updates and emit at max 1/interval
|
||||
|
||||
@@ -113,5 +113,7 @@ namespace mbts.co2
|
||||
[DllImport("libspark")]
|
||||
internal static extern void co2_get_zero_and_nan_book(ref NativePriceLevel nps);
|
||||
|
||||
[DllImport("libspark")]
|
||||
internal static extern void co2_get_sample_datetime(ref utils.LLDateTime now, ref utils.LLDateTime utc_now);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace bts.test {
|
||||
public static class ConsoleEx {
|
||||
@@ -21,17 +23,34 @@ namespace bts.test {
|
||||
}
|
||||
|
||||
public static T prompt<T>(string prompt, params T[] options) {
|
||||
Console.WriteLine(prompt);
|
||||
for (int x = 1; x <= options.Length; ++x) {
|
||||
return ConsoleEx.prompt(prompt, (IEnumerable<T>)options);
|
||||
}
|
||||
public static T prompt<T>(string prompt, IEnumerable<T> options) {
|
||||
WriteLine(prompt, ConsoleColor.Yellow);
|
||||
for (int x = 0; x < options.Count(); ++x) {
|
||||
Write($"{x}. ", ConsoleColor.Yellow);
|
||||
WriteLine(options[x].ToString());
|
||||
WriteLine(options.ElementAt(x).ToString());
|
||||
}
|
||||
int response;
|
||||
do {
|
||||
Int32.TryParse(Console.ReadKey().ToString(), out response);
|
||||
if (response > options.Length) response = 0;
|
||||
} while (response == 0);
|
||||
return options[response];
|
||||
int response = 0;
|
||||
bool success = false;
|
||||
while(!success) {
|
||||
var key = Console.ReadKey();
|
||||
success = Int32.TryParse(key.KeyChar.ToString(), out response);
|
||||
if (response > options.Count()) success = false;
|
||||
}
|
||||
var value = options.ElementAt(response);
|
||||
Console.WriteLine(" >> " + value.ToString());
|
||||
Console.WriteLine();
|
||||
return value;
|
||||
}
|
||||
|
||||
public static string input_prompt(string prompt, string default_result) {
|
||||
Write(prompt + " (", ConsoleColor.Yellow);
|
||||
Write(default_result, ConsoleColor.White);
|
||||
Write(") ", ConsoleColor.Yellow);
|
||||
var result = Console.ReadLine();
|
||||
if (string.IsNullOrWhiteSpace(result)) return default_result;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -43,6 +43,9 @@ namespace bts.test {
|
||||
if (ue.InnerException is AssertFailture af) {
|
||||
result.Message = $"Assertion Failure:\n{af.Message}\nLocation:{af._filename}:{af._line}";
|
||||
}
|
||||
else if (ue.InnerException != null) {
|
||||
result.Message = $"Unexpected error: {ue.InnerException}";
|
||||
}
|
||||
else {
|
||||
result.Message = $"Unexpected error: {ue.Message}\n\n{ue.StackTrace}";
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace mbts {
|
||||
@@ -24,20 +23,14 @@ namespace mbts {
|
||||
return range.Skip(index).First();
|
||||
}
|
||||
|
||||
[DllImport("libspark")]
|
||||
private static extern void ll_to_parts(UInt64 ll_time, out int year, out int month, out int day, out int hour, out int minute, out int second, out int ms);
|
||||
|
||||
public static DateTime lldatetime(this ulong ll) {
|
||||
int year, month, day, hour, minute, second, ms;
|
||||
ll_to_parts(ll, out year, out month, out day, out hour, out minute, out second, out ms);
|
||||
if (year > 0) {
|
||||
return new DateTime(year, month, day, hour, minute, second, ms, DateTimeKind.Utc).ToLocalTime();
|
||||
}
|
||||
return DateTime.MinValue;
|
||||
// Warning: Do not remove these consts, or try to simplify this method.
|
||||
// System.DateTime() requires bits 63-64 to be empty in the "DateTime(long ticks)" constructor.
|
||||
// The only way to set the DateTimeKind is through the "DateTime(long ticks, DateTimeKind kind)" constructor.
|
||||
// (Yes, it is annoying. Internally, System.DateTime stores the DateTimeKind in bits 63-64 - in the exact format we already have!!!)
|
||||
const ulong ticks_mask = 0x3FFFFFFFFFFFFFFF;
|
||||
const int kind_shift = 62;
|
||||
return new DateTime((long)(ll & ticks_mask), (DateTimeKind)(ll >> kind_shift));
|
||||
}
|
||||
|
||||
public static DateTime lldatetime(this long ll) { return ((ulong)ll).lldatetime(); }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,6 +18,7 @@ namespace mbts.utils
|
||||
}
|
||||
|
||||
public static implicit operator DateTime (LLDateTime time) { return time.to_datetime(); }
|
||||
public static implicit operator LLDateTime (DateTime time) { return new LLDateTime((ulong)time.Ticks); }
|
||||
|
||||
public static LLDateTime now() {
|
||||
return new LLDateTime(BTSNativeMethods.ll_now());
|
||||
|
||||
@@ -56,6 +56,11 @@ namespace bts.utils {
|
||||
if (value > max) return max;
|
||||
return value;
|
||||
}
|
||||
public static long within_range(long min, long max, long value) {
|
||||
if (value < min) return min;
|
||||
if (value > max) return max;
|
||||
return value;
|
||||
}
|
||||
public static double within_range(double min, double max, double value) {
|
||||
if (value < min) return min;
|
||||
if (value > max) return max;
|
||||
|
||||
@@ -112,7 +112,7 @@ namespace mbts.co2.tests
|
||||
Assert.IsTrue(last.price == 4.2);
|
||||
Assert.IsTrue(last.size == 101);
|
||||
Assert.IsTrue(entry.total_volume == 12345);
|
||||
Assert.IsTrue(compare_date(entry.last_trade_time, DateTime.Now));
|
||||
Assert.IsTrue(compare_date(entry.last_trade_time, DateTime.UtcNow));
|
||||
//// Settlement
|
||||
Assert.AreEqual(entry.settlement_price, 3.3);
|
||||
Assert.IsTrue(entry.settlement_date == new DateTime(2015, 9, 3));
|
||||
@@ -251,6 +251,25 @@ namespace mbts.co2.tests
|
||||
Assert.IsTrue(double.IsNaN(npl.bid.price));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void lldatetime_test() {
|
||||
utils.LLDateTime now = DateTime.MinValue, utc_now = DateTime.MinValue;
|
||||
Co2NativeTestMethods.co2_get_sample_datetime(ref now, ref utc_now);
|
||||
DateTime dt_now = now;
|
||||
DateTime dt_utc_now = utc_now;
|
||||
Assert.AreEqual(dt_now.ToShortDateString(), DateTime.Now.ToShortDateString());
|
||||
Assert.AreEqual(dt_utc_now.ToShortDateString(), DateTime.UtcNow.ToShortDateString());
|
||||
void assert_about_equal(int a, int b) {
|
||||
Assert.That(Math.Abs(a - b) <= 1);
|
||||
}
|
||||
|
||||
assert_about_equal(dt_now.Hour, DateTime.Now.Hour);
|
||||
assert_about_equal(dt_now.Minute, DateTime.Now.Minute);
|
||||
assert_about_equal(dt_now.Second, DateTime.Now.Second);
|
||||
|
||||
assert_about_equal(dt_utc_now.Hour, DateTime.UtcNow.Hour);
|
||||
assert_about_equal(dt_utc_now.Minute, DateTime.UtcNow.Minute);
|
||||
assert_about_equal(dt_utc_now.Second, DateTime.UtcNow.Second);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -227,5 +227,15 @@ namespace mbts.api.Test {
|
||||
await validate_naggle(raw);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void test_merge_begin_end_state() {
|
||||
int key(string s) => s[0];
|
||||
var empty = new string[0];
|
||||
var a2 = new Update<string, int>(new[] { "a2" }, empty, new[] { new Change<string>("a1", "a2") }, empty, key);
|
||||
var a3 = new Update<string, int>(new[] { "a3" }, empty, new[] { new Change<string>("a2", "a3") }, empty, key);
|
||||
var merged = a2.merge(a3);
|
||||
Assert.SequenceEqual(new[] { new Change<string>("a1", "a3") }, merged.changes);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using ManyConsole;
|
||||
using System.Reflection;
|
||||
|
||||
namespace bts {
|
||||
|
||||
public class SettinsManagerCommand : ConsoleCommand {
|
||||
public SettinsManagerCommand() {
|
||||
IsCommand("SettingsManager", "Copy workspaces around");
|
||||
}
|
||||
|
||||
public override int Run(string[] remainingArguments) {
|
||||
var asm = Assembly.Load("spark");
|
||||
var type = asm.GetType("bts.settings.SettingsManagerTool");
|
||||
var run = type.GetMethod("run", BindingFlags.Static | BindingFlags.Public);
|
||||
run.Invoke(null, new object[0]);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
set(sources api/apicore.proto;api/apigateway.proto;api/apioptions.proto;api/core/ApiClientInterop.cpp;api/core/ApiServerInterop.cpp;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/services/AdminService.proto;api/services/ApiUnitTestService.proto;api/services/ErrorReportService.proto;api/services/HelloService.proto;api/services/MarketDataRelay.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/services/UserService.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/Executor.conf;config/defaults/MarketData.conf;config/defaults/Ports.conf;config/defaults/Symbols.conf;config/defaults/cpp_config.conf;config/defaults/dynamic_orders.conf;config/defaults/edge.conf;config/defaults/expiration_display_offset.conf;config/defaults/jetfix.conf;config/defaults/logging.conf;config/defaults/orders_fills_cleanup.conf;config/defaults/spreader.conf;config/defaults/testkeys.conf;config/defaults/toggles.conf;config/extensions/extensions.proto;config/invalid/invalid.proto;config/protos/ExchangeSimCfg.proto;config/protos/MarketData.proto;config/protos/Ports.proto;config/protos/Symbols.proto;config/protos/UI.proto;config/protos/cpp_config.proto;config/protos/dynamic_orders.proto;config/protos/environment.proto;config/protos/expiration_display_offset.proto;config/protos/jetfix.proto;config/protos/logging.proto;config/protos/orders_fills_cleanup.proto;config/protos/quickbus_cfg.proto;config/protos/spreader.proto;config/protos/system.proto;config/protos/testkeys.proto;config/protos/toggles.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/tests/domain_tests.cpp;expression/BooleanGrammar.cpp;expression/ExpressionEvaluator.cpp;expression/ParserExceptions.cpp;expression/Value.cpp;expression/tests/expression_parser_tests.cpp;logging/LoggerHub.cpp;logging/Sink.cpp;logging/logging.cpp;logging/tests/logging_tests.cpp;md/Diag/MDPriceLantencyTester.cpp;md/Diag/ShmDumper.cpp;md/MDConsumer.cpp;md/MDConsumerHandle.cpp;md/MDConsumerInterop.cpp;md/MDProducer.cpp;md/MDProducerHandle.cpp;md/MDRecoverableConsumer.cpp;md/MemorySegmentUtils.cpp;md/PlatformLoader.cpp;md/PriceSize.cpp;md/SecurityDB.cpp;md/SecurityIDRecorder.cpp;md/SettlementRecorder.cpp;md/Shm/FixedSecurity.cpp;md/SyntheticSecurityService.cpp;md/cme/MDP3Utils.cpp;md/common.cpp;md/protos/co2.proto;md/relay/QueryServer.cpp;md/relay/RelayClient.cpp;md/relay/RelayRecorder.cpp;md/test/Co2ManagedTestHelper.cpp;md/test/Co2TestInstrument.cpp;md/test/md2_tests.cpp;md/test/synthetic_book_merge_tests.cpp;os/os.cpp;os/tests/os_tests.cpp;quickbus/core/LocalNode.cpp;quickbus/core/Quickbus.cpp;quickbus/core/SubscriptionID.cpp;quickbus/core/TableID.cpp;quickbus/message_model/Update.cpp;quickbus/message_model/merge_updates.cpp;quickbus/network/QBConnection.cpp;quickbus/network/QBConnectionManager.cpp;quickbus/network/endpoint_description.cpp;quickbus/network/quickbus_msgs.proto;quickbus/pubsub/TablePublisher.cpp;quickbus/pubsub/TableSubscriber.cpp;quickbus/pubsub/tests/qb_pubsub_tests.cpp;quickbus/routing/INode.cpp;quickbus/routing/NodeID.cpp;quickbus/routing/Router.cpp;quickbus/rpc/RPCClient.cpp;quickbus/rpc/RPCServer.cpp;quickbus/rpc/RPCSessionID.cpp;quickbus/rpc/rpc_utils.cpp;quickbus/rpc/tests/qb_rpc_tests.cpp;quickbus/tests/quickbus_tests.cpp;sparkproto/SparkProtos.proto;testing/catch_test_runner.cpp;utils/Dispatcher.cpp;utils/HTTPStream.cpp;utils/PBUtils.cpp;utils/Puid.cpp;utils/SecureLogon.cpp;utils/SharedMemory.cpp;utils/Sqlite_utils.cpp;utils/Uuid.cpp;utils/datetime.cpp;utils/download.cpp;utils/file_utils.cpp;utils/init_spark_lib.cpp;utils/is_unittest_environment.cpp)
|
||||
set(sources api/apicore.proto;api/apigateway.proto;api/apioptions.proto;api/core/ApiClientInterop.cpp;api/core/ApiServerInterop.cpp;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/services/AdminService.proto;api/services/ApiUnitTestService.proto;api/services/ErrorReportService.proto;api/services/HelloService.proto;api/services/MarketDataRelay.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/services/UserService.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/Executor.conf;config/defaults/MarketData.conf;config/defaults/Ports.conf;config/defaults/Symbols.conf;config/defaults/cpp_config.conf;config/defaults/dynamic_orders.conf;config/defaults/edge.conf;config/defaults/expiration_display_offset.conf;config/defaults/jetfix.conf;config/defaults/logging.conf;config/defaults/orders_fills_cleanup.conf;config/defaults/spreader.conf;config/defaults/testkeys.conf;config/defaults/toggles.conf;config/extensions/extensions.proto;config/invalid/invalid.proto;config/protos/ExchangeSimCfg.proto;config/protos/MarketData.proto;config/protos/Ports.proto;config/protos/Symbols.proto;config/protos/UI.proto;config/protos/cpp_config.proto;config/protos/dynamic_orders.proto;config/protos/expiration_display_offset.proto;config/protos/jetfix.proto;config/protos/logging.proto;config/protos/orders_fills_cleanup.proto;config/protos/quickbus_cfg.proto;config/protos/spreader.proto;config/protos/system.proto;config/protos/testkeys.proto;config/protos/toggles.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/tests/domain_tests.cpp;expression/BooleanGrammar.cpp;expression/ExpressionEvaluator.cpp;expression/ParserExceptions.cpp;expression/Value.cpp;expression/tests/expression_parser_tests.cpp;logging/LoggerHub.cpp;logging/Sink.cpp;logging/logging.cpp;logging/tests/logging_tests.cpp;md/Diag/MDPriceLantencyTester.cpp;md/Diag/ShmDumper.cpp;md/MDConsumer.cpp;md/MDConsumerHandle.cpp;md/MDConsumerInterop.cpp;md/MDProducer.cpp;md/MDProducerHandle.cpp;md/MDRecoverableConsumer.cpp;md/MemorySegmentUtils.cpp;md/PlatformLoader.cpp;md/PriceSize.cpp;md/SecurityDB.cpp;md/SecurityIDRecorder.cpp;md/SettlementRecorder.cpp;md/Shm/FixedSecurity.cpp;md/SyntheticSecurityService.cpp;md/cme/MDP3Utils.cpp;md/common.cpp;md/protos/co2.proto;md/relay/QueryServer.cpp;md/relay/RelayClient.cpp;md/relay/RelayRecorder.cpp;md/test/Co2ManagedTestHelper.cpp;md/test/Co2TestInstrument.cpp;md/test/md2_tests.cpp;md/test/synthetic_book_merge_tests.cpp;os/os.cpp;os/tests/os_tests.cpp;quickbus/core/LocalNode.cpp;quickbus/core/Quickbus.cpp;quickbus/core/SubscriptionID.cpp;quickbus/core/TableID.cpp;quickbus/message_model/Update.cpp;quickbus/message_model/merge_updates.cpp;quickbus/network/QBConnection.cpp;quickbus/network/QBConnectionManager.cpp;quickbus/network/endpoint_description.cpp;quickbus/network/quickbus_msgs.proto;quickbus/pubsub/TablePublisher.cpp;quickbus/pubsub/TableSubscriber.cpp;quickbus/pubsub/tests/qb_pubsub_tests.cpp;quickbus/routing/INode.cpp;quickbus/routing/NodeID.cpp;quickbus/routing/Router.cpp;quickbus/rpc/RPCClient.cpp;quickbus/rpc/RPCServer.cpp;quickbus/rpc/RPCSessionID.cpp;quickbus/rpc/rpc_utils.cpp;quickbus/rpc/tests/qb_rpc_tests.cpp;quickbus/tests/quickbus_tests.cpp;sparkproto/SparkProtos.proto;testing/catch_test_runner.cpp;utils/Dispatcher.cpp;utils/HTTPStream.cpp;utils/PBUtils.cpp;utils/Puid.cpp;utils/SecureLogon.cpp;utils/SharedMemory.cpp;utils/Sqlite_utils.cpp;utils/Uuid.cpp;utils/datetime.cpp;utils/download.cpp;utils/file_utils.cpp;utils/init_spark_lib.cpp;utils/is_unittest_environment.cpp)
|
||||
@@ -109,6 +109,15 @@ message TickOrderDefinition {
|
||||
message LeanOrderDefinition {
|
||||
spark.QuantityMeasure cross_threshold = 1;
|
||||
uint32 max_chase_ticks = 2;
|
||||
/*deprecated*/ bool detach_on_toward = 4;
|
||||
enum OnToward
|
||||
{
|
||||
NO_CHANGE = 0; //continue to lean on initial inside market
|
||||
CANCEL = 1; //cancel algo and order
|
||||
DETACH = 2; //cancel algo but leave order working
|
||||
REPRICE = 3; //lean on new inside market
|
||||
}
|
||||
OnToward trade_toward_behavior = 5;
|
||||
CustomOrderDefinition child = 3;
|
||||
}
|
||||
message JoinOrderDefinition {
|
||||
|
||||
@@ -234,7 +234,7 @@ bool ConfigPropertyEvaluator::get_value(const std::string& property, Value* out_
|
||||
tokenizer<char_separator<char> >::iterator iter = tokens.begin (), end = tokens.end ();
|
||||
|
||||
// The first token is the key name
|
||||
const proto::Message* key = _config.try_get_message(*iter);
|
||||
const proto::Message* key = _config.try_get_message_or_default(*iter);
|
||||
if (!key) return false;
|
||||
|
||||
// 2) Drill down using reflection to find the named property
|
||||
|
||||
@@ -616,6 +616,19 @@ const proto::Message* ConfigurationState::try_get_message(const std::string& pro
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const proto::Message* ConfigurationState::try_get_message_or_default(const std::string& property_name) {
|
||||
const auto pmsg = try_get_message(property_name);
|
||||
if (pmsg) {
|
||||
return pmsg;
|
||||
}
|
||||
// If we can't find a value we should return a default instance
|
||||
const auto* descriptor = get_descriptor_by_name(property_name);
|
||||
if (descriptor) {
|
||||
return proto::MessageFactory::generated_factory()->GetPrototype(descriptor);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const proto::Message& ConfigurationState::get_message(const proto::Descriptor* property_desc)
|
||||
{
|
||||
if (!is_valid_key_descriptor(property_desc))
|
||||
|
||||
@@ -59,6 +59,7 @@ namespace internal {
|
||||
|
||||
const proto::Message& get_message(const proto::Descriptor* property_desc);
|
||||
const proto::Message* try_get_message(const std::string& property_name);
|
||||
const proto::Message* try_get_message_or_default(const std::string& property_name);
|
||||
|
||||
bool is_initialized() { return _is_initialized; }
|
||||
|
||||
|
||||
@@ -76,16 +76,7 @@ extern "C" BTS_EXPORT const char* get_buffer(const char* key_name, int& out_leng
|
||||
|
||||
extern "C" BTS_EXPORT const proto::Message* get_named(const char* key_name)
|
||||
{
|
||||
const auto* pmessage = config_state.try_get_message(key_name);
|
||||
if (pmessage) {
|
||||
return pmessage;
|
||||
}
|
||||
// If we can't find a value we should return a default instance
|
||||
const auto* descriptor = config_state.get_descriptor_by_name(key_name);
|
||||
if (descriptor) {
|
||||
return proto::MessageFactory::generated_factory()->GetPrototype(descriptor);
|
||||
}
|
||||
return nullptr;
|
||||
return config_state.try_get_message_or_default(key_name);
|
||||
}
|
||||
|
||||
BTS_EXPORT std::string get_macro_string(const char* name) { return config_state.get_macro_string(name); }
|
||||
|
||||
@@ -26,6 +26,26 @@ SymbolList {
|
||||
}
|
||||
}
|
||||
|
||||
Symbols {
|
||||
rule: "DeploymentType.type == 'DEMO' and System.platform == 'windows'"
|
||||
value {
|
||||
sim {
|
||||
future: @SymbolList.symbol
|
||||
spread: @SymbolList.symbol
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Symbols {
|
||||
rule: "DeploymentType.type == 'PROD' and System.platform == 'windows'"
|
||||
value {
|
||||
glbx {
|
||||
future: @SymbolList.symbol
|
||||
spread: @SymbolList.symbol
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Symbols {
|
||||
value {
|
||||
glbx {
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
syntax='proto2';
|
||||
package bts.config;
|
||||
|
||||
message Deployment {
|
||||
// Acceptable values:
|
||||
// DEV_CHAPEL_HILL, DEV_CHICAGO, DEMO_GT, QA, CUSTOMER
|
||||
optional string type = 1 [default="DEV_CHAPEL_HILL"];
|
||||
}
|
||||
@@ -132,7 +132,7 @@ int SocketConnection::write(WriteVec const &vec, size_t vec_size,
|
||||
"perf_event:tick_to_order s2s_us:{} fix_socket_r:{} md:{} "
|
||||
"md_src:{} spreader_on_book:{} spreader_modify_order:{} "
|
||||
"spreader_on_child_order_update:{} "
|
||||
"order_update:{} exe_gw:{} fix_socket_w:{} md_sn:{}",
|
||||
"order_update:{} exe_gw:{} fix_socket_w:{} md_sn:{} order_key:{}",
|
||||
s2s.microseconds(), trace_cpy.fix_socket_r.to_uint64(),
|
||||
trace_cpy.md.to_uint64(), trace_cpy.md_src.to_uint64(),
|
||||
trace_cpy.spreader_on_book.to_uint64(),
|
||||
@@ -140,7 +140,8 @@ int SocketConnection::write(WriteVec const &vec, size_t vec_size,
|
||||
trace_cpy.spreader_on_child_order_update.to_uint64(),
|
||||
trace_cpy.order_update.to_uint64(),
|
||||
trace_cpy.exe_gw.to_uint64(), trace_cpy.fix_socket_w.to_uint64(),
|
||||
trace_cpy.md_sn);
|
||||
trace_cpy.md_sn,
|
||||
trace_cpy.order_key);
|
||||
_tick_to_order->observe(s2s.nanoseconds());
|
||||
}
|
||||
|
||||
@@ -150,7 +151,7 @@ int SocketConnection::write(WriteVec const &vec, size_t vec_size,
|
||||
"perf_event:fill_to_order s2s_us:{} fix_socket_r:{} md:{} md_src:{} "
|
||||
"spreader_on_book:{} spreader_modify_order:{} "
|
||||
"spreader_on_child_order_update:{} "
|
||||
"order_update:{} exe_gw:{} fix_socket_w:{} md_sn:{}",
|
||||
"order_update:{} exe_gw:{} fix_socket_w:{} md_sn:{} order_key:{}",
|
||||
s2s.microseconds(), trace_cpy.fix_socket_r.to_uint64(),
|
||||
trace_cpy.md.to_uint64(), trace_cpy.md_src.to_uint64(),
|
||||
trace_cpy.spreader_on_book.to_uint64(),
|
||||
@@ -158,7 +159,8 @@ int SocketConnection::write(WriteVec const &vec, size_t vec_size,
|
||||
trace_cpy.spreader_on_child_order_update.to_uint64(),
|
||||
trace_cpy.order_update.to_uint64(),
|
||||
trace_cpy.exe_gw.to_uint64(), trace_cpy.fix_socket_w.to_uint64(),
|
||||
trace_cpy.md_sn);
|
||||
trace_cpy.md_sn,
|
||||
trace.order_key);
|
||||
_fill_to_order->observe(s2s.nanoseconds());
|
||||
}
|
||||
|
||||
|
||||
@@ -76,8 +76,7 @@ string dump_security(const FixedSecurity& security) {
|
||||
res << "'main_frac': " << (int)security.cme_main_fraction << ", ";
|
||||
res << "'sub_fract': " << (int)security.cme_sub_fraction << ", ";
|
||||
res << "'price_format': " << (int)security.cme_price_display_format << ", ";
|
||||
|
||||
res << "'market_segment': " << security.cme_market_segment_id << ", ";
|
||||
res << "'market_segment': " << (int)security.cme_market_segment_id << ", ";
|
||||
|
||||
res << "'price_break': " << security.price_break << ", ";
|
||||
res << "'low_tick': " << security.low_tick << ", ";
|
||||
|
||||
@@ -141,6 +141,10 @@ extern "C" BTS_EXPORT void co2_get_zero_and_nan_book(PriceLevel& price) {
|
||||
price = { PriceSize::NO_MARKET, PriceSize::from_double<7>(0,1) };
|
||||
}
|
||||
|
||||
extern "C" BTS_EXPORT void co2_get_sample_datetime(datetime::LLDateTime& now, datetime::LLDateTime& utc_now) {
|
||||
now = datetime::ll_local_now();
|
||||
utc_now = datetime::ll_now();
|
||||
}
|
||||
|
||||
}}}//namespace
|
||||
#endif
|
||||
@@ -24,8 +24,7 @@ struct OrderStore::PImpl {
|
||||
PImpl()
|
||||
: state(State::WAIT_SEC), known_orders(),
|
||||
store(new persistence::SqliteStore("orders")), order_stream(),
|
||||
soft_cleanup_enabled (config::get<config::toggles::OrdersSoftCleanup>().enabled()),
|
||||
cutoff_time(SODPosition::get_orders_start_time())
|
||||
soft_cleanup_enabled (config::get<config::toggles::OrdersSoftCleanup>().enabled())
|
||||
{}
|
||||
|
||||
State state;
|
||||
@@ -76,6 +75,8 @@ void OrderStore::init() {
|
||||
auto all = me->store->retrieve_all()[orders_ns];
|
||||
LOG(persist, Info, "Finished loading all {} orders", all.size());
|
||||
|
||||
me->cutoff_time=SODPosition::get_order_cutoff_time(all);
|
||||
|
||||
for (auto const &kvp : all)
|
||||
if (utils::pb::from_string<api::orderman::OrderRecord>(
|
||||
kvp.second, &rec)) {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "lib/config/protos/toggles.pb.h"
|
||||
#include "lib/config/protos/orders_fills_cleanup.pb.h"
|
||||
#include "lib/config/config.h"
|
||||
#include "lib/utils/PBUtils.h"
|
||||
#include <sys/time.h>
|
||||
#include <sstream>
|
||||
|
||||
@@ -18,12 +19,14 @@ _sod_time(_get_fills_sod_time()){
|
||||
_enabled, _sod_time.tv_sec);
|
||||
}
|
||||
|
||||
// returns the timeval for today's hh,mm,ss
|
||||
// timeval is the microsecons from epoch)
|
||||
timeval SODPosition::_to_timeval(uint16_t hh, uint16_t mm, uint16_t ss){
|
||||
bts_assert(hh<24);
|
||||
bts_assert(mm<60);
|
||||
bts_assert(ss<60);
|
||||
time_t secs;
|
||||
time (&secs); // seconds since epoch
|
||||
time (&secs); // seconds since epoch (utc)
|
||||
struct tm* ptm = gmtime (&secs);
|
||||
ptm->tm_hour=hh;
|
||||
ptm->tm_min=mm;
|
||||
@@ -37,9 +40,8 @@ timeval SODPosition::_to_timeval(uint16_t hh, uint16_t mm, uint16_t ss){
|
||||
|
||||
timeval SODPosition::_subtract_days(const timeval& tv, uint32_t n_days){
|
||||
uint32_t secs_in_day=86400*n_days;
|
||||
bts_assert(tv.tv_sec>secs_in_day);
|
||||
timeval rv=tv;
|
||||
rv.tv_sec=rv.tv_sec-secs_in_day;
|
||||
rv.tv_sec=(rv.tv_sec > secs_in_day) ? rv.tv_sec-secs_in_day : 0;
|
||||
return rv;
|
||||
}
|
||||
|
||||
@@ -69,6 +71,42 @@ timeval SODPosition::get_orders_start_time(){
|
||||
return sod;
|
||||
}
|
||||
|
||||
timeval SODPosition::get_order_cutoff_time(
|
||||
const std::map<std::string,std::string>& order_map){
|
||||
//returns true is t1 >= t2
|
||||
auto compare_ts = [&](google::protobuf::Timestamp t1, google::protobuf::Timestamp t2) -> bool
|
||||
{
|
||||
if (t1.seconds() > t2.seconds())
|
||||
return true;
|
||||
else if (t1.seconds() < t2.seconds())
|
||||
return false;
|
||||
else
|
||||
return t1.nanos() >= t2.nanos();
|
||||
};
|
||||
google::protobuf::Timestamp max_ts;
|
||||
max_ts.set_seconds(0);
|
||||
max_ts.set_nanos(0);
|
||||
|
||||
for (auto const &kvp : order_map){
|
||||
OrderRecord trec;
|
||||
if (utils::pb::from_string<api::orderman::OrderRecord>(kvp.second, &trec)){
|
||||
const auto& ts = trec.state().created();
|
||||
if(compare_ts(ts,max_ts))
|
||||
(&max_ts)->CopyFrom(ts);
|
||||
}
|
||||
else
|
||||
LOG(OrderMan, Error,
|
||||
"Could not recover order {} from storage. "
|
||||
"This should be investigated",
|
||||
kvp.first);
|
||||
}
|
||||
timeval rv;
|
||||
rv.tv_sec=max_ts.seconds();
|
||||
rv.tv_usec=static_cast<double>(max_ts.nanos())/1000;
|
||||
const auto& orders_cleanup = config::get<config::OrdersSoftCleanupCutoff>();
|
||||
return _subtract_days(rv,orders_cleanup.extra_days());
|
||||
}
|
||||
|
||||
std::string SODPosition::_sod_fill_id(const Fill& fill,api::Side side){
|
||||
//sod:acct:long/short:sec
|
||||
std::ostringstream oss;
|
||||
@@ -80,12 +118,12 @@ std::string SODPosition::_sod_fill_id(const Fill& fill,api::Side side){
|
||||
void SODPosition::_merge_pos(Fill& pos, const Fill& fill){
|
||||
bts_assert(pos.security_key()==fill.security_key());
|
||||
bts_assert(pos.account()==fill.account());
|
||||
bts_assert(pos.side()==fill.side());
|
||||
pos.set_last_price(fill.last_price());
|
||||
bts_assert(pos.side()==fill.side());
|
||||
double total_qty=pos.last_shares()+fill.last_shares();
|
||||
double avg_price=(pos.avg_price()*pos.last_shares()+
|
||||
fill.avg_price()*fill.last_shares())/total_qty;
|
||||
fill.last_price()*fill.last_shares())/total_qty;
|
||||
pos.set_avg_price(avg_price);
|
||||
pos.set_last_price(avg_price);
|
||||
pos.set_last_shares(total_qty);
|
||||
}
|
||||
|
||||
@@ -98,6 +136,7 @@ void SODPosition::_zero_pos(Fill& pos, const Fill& fill, api::Side side){
|
||||
pos.set_fill_kind(Fill::FILL_KIND_SOD);
|
||||
pos.set_order_key(0);
|
||||
pos.set_fill_key(_sod_fill_id(fill,side));
|
||||
pos.set_last_shares(0);
|
||||
}
|
||||
|
||||
bool SODPosition::add_fill_to_sod_pos(const Fill& fill){
|
||||
|
||||
@@ -31,9 +31,10 @@ public:
|
||||
SODPosition();
|
||||
// all inactive orders before this time are not published
|
||||
static timeval get_orders_start_time();
|
||||
static timeval get_order_cutoff_time(const std::map<std::string,std::string>& ordermap);
|
||||
bool add_fill_to_sod_pos(const Fill& fill);
|
||||
void add_sod_pos_as_fill_events(std::vector<StoredFillEvent>& fill_events,
|
||||
std::map<SecurityKey, const api::Security*> sec_map);
|
||||
std::map<SecurityKey, const api::Security*> sec_map);
|
||||
};
|
||||
|
||||
}}
|
||||
@@ -16,6 +16,7 @@ public:
|
||||
utils::TimeStamp exe_gw ;
|
||||
utils::TimeStamp fix_socket_w ;
|
||||
utils::TimeStamp spreader_modify_order ;
|
||||
uint32_t order_key = 0;
|
||||
uint16_t md_sn = 0;
|
||||
};
|
||||
|
||||
|
||||
@@ -271,7 +271,10 @@ NativeOrder::~NativeOrder() {}
|
||||
|
||||
void NativeOrder::on_activate(Price p, Quantity q,
|
||||
bts::orderman::OrderServices &services,
|
||||
PerfTrace const&trace) {
|
||||
PerfTrace const&t) {
|
||||
auto trace = t;
|
||||
trace.order_key = key().value();
|
||||
|
||||
tracepoint(bts_trace, trace_order, (char *)"on_activate", 0, trace.md_sn, 0);
|
||||
bts_assert(!me->services);
|
||||
me->services = &services;
|
||||
@@ -454,6 +457,7 @@ void NativeOrder::on_activate(Price p, Quantity q,
|
||||
void NativeOrder::on_update(Price p, Quantity q, PerfTrace const &t) {
|
||||
auto trace = t;
|
||||
trace.order_update = utils::TimeStamp::now();
|
||||
trace.order_key = key().value();
|
||||
tracepoint(bts_trace, trace_order, (char *)"on_update", 0, trace.md_sn, 0);
|
||||
|
||||
switch (me->state.status()) {
|
||||
|
||||
@@ -408,14 +408,14 @@ void BookAlgo::report_state()
|
||||
me->services->report_state(merged_state);
|
||||
}
|
||||
|
||||
void BookAlgo::payup(const md::PriceSize& opposite, uint32_t cross_ticks)
|
||||
void BookAlgo::payup(const md::PriceSize& opposite, const bts::orderman::BookUpdateEvent&, uint32_t cross_ticks)
|
||||
{
|
||||
double new_price = opposite.price() + me->tick_size * cross_ticks;
|
||||
me->child.update(Price(new_price), Quantity(this_state().quantity()));
|
||||
report_state();
|
||||
}
|
||||
|
||||
void BookAlgo::paydown(const md::PriceSize& opposite, uint32_t cross_ticks)
|
||||
void BookAlgo::paydown(const md::PriceSize& opposite, const bts::orderman::BookUpdateEvent&, uint32_t cross_ticks)
|
||||
{
|
||||
double new_price = opposite.price() - me->tick_size * cross_ticks;
|
||||
me->child.update(Price(new_price), Quantity(this_state().quantity()));
|
||||
|
||||
@@ -42,8 +42,8 @@ protected:
|
||||
virtual void transition(api::orderman::OrderStatus new_state, std::string = "");
|
||||
virtual void report_state();
|
||||
|
||||
virtual void payup(const md::PriceSize&, uint32_t cross_ticks = 0);
|
||||
virtual void paydown(const md::PriceSize&, uint32_t cross_ticks = 0);
|
||||
virtual void payup(const md::PriceSize&, const bts::orderman::BookUpdateEvent&, uint32_t cross_ticks = 0);
|
||||
virtual void paydown(const md::PriceSize&, const bts::orderman::BookUpdateEvent&, uint32_t cross_ticks = 0);
|
||||
void detach(bts::api::orderman::OrderStatus status_after_detach = bts::api::orderman::ORDER_STATUS_CANCELED);
|
||||
private:
|
||||
struct PImpl;
|
||||
|
||||
@@ -12,12 +12,15 @@ using namespace api::orderman;
|
||||
|
||||
struct LeanOrder::PImpl
|
||||
{
|
||||
PImpl() {}
|
||||
PImpl() : cross_threshold(), max_chase_ticks(0), trade_toward_behavior(LeanOrderDefinition_OnToward_NO_CHANGE), opposite_at_inception(), num_chases(0)
|
||||
{}
|
||||
|
||||
spark::QuantityMeasure cross_threshold;
|
||||
uint32_t max_chase_ticks;
|
||||
LeanOrderDefinition_OnToward trade_toward_behavior;
|
||||
|
||||
md::PriceSize opposite_at_inception;
|
||||
uint32_t num_chases;
|
||||
};
|
||||
|
||||
|
||||
@@ -34,25 +37,40 @@ void LeanOrder::setup_child(const api::orderman::OrderDefinition& this_def, cons
|
||||
|
||||
me->cross_threshold = lean_def.cross_threshold();
|
||||
me->max_chase_ticks = lean_def.max_chase_ticks();
|
||||
me->trade_toward_behavior = lean_def.trade_toward_behavior();
|
||||
}
|
||||
|
||||
void LeanOrder::payup(const md::PriceSize& opposite, uint32_t cross_ticks)
|
||||
void LeanOrder::payup(const md::PriceSize&, const bts::orderman::BookUpdateEvent& book, uint32_t cross_ticks /*= 0*/)
|
||||
{
|
||||
double new_price = opposite.price() + tick_size() * cross_ticks;
|
||||
double new_price = me->opposite_at_inception.price() + tick_size() * cross_ticks;
|
||||
double limit = me->opposite_at_inception.price() + tick_size() * me->max_chase_ticks;
|
||||
if(new_price <= limit)
|
||||
update_child(Price(new_price), Quantity(this_state().quantity()));
|
||||
else
|
||||
{
|
||||
update_child(Price(limit), Quantity(this_state().quantity()));
|
||||
update_child(Price(limit), Quantity(this_state().quantity()));
|
||||
detach();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const bts::orderman::BookSide& sellbook = book.book.side(api::Side::SIDE_SELL);
|
||||
for(const auto& i : sellbook)
|
||||
{
|
||||
me->opposite_at_inception = i;
|
||||
if(me->opposite_at_inception.price() > new_price)
|
||||
break;
|
||||
}
|
||||
if(me->opposite_at_inception.price() <= new_price)
|
||||
detach();
|
||||
me->num_chases += cross_ticks+1;
|
||||
if(me->num_chases >= me->max_chase_ticks)
|
||||
detach();
|
||||
report_state();
|
||||
}
|
||||
|
||||
void LeanOrder::paydown(const md::PriceSize& opposite, uint32_t cross_ticks)
|
||||
void LeanOrder::paydown(const md::PriceSize&, const bts::orderman::BookUpdateEvent& book, uint32_t cross_ticks /*= 0*/)
|
||||
{
|
||||
double new_price = opposite.price() - tick_size() * cross_ticks;
|
||||
double new_price = me->opposite_at_inception.price() - tick_size() * cross_ticks;
|
||||
double limit = me->opposite_at_inception.price() - tick_size() * me->max_chase_ticks;
|
||||
if(new_price >= limit)
|
||||
update_child(Price(new_price), Quantity(this_state().quantity()));
|
||||
@@ -60,10 +78,50 @@ void LeanOrder::paydown(const md::PriceSize& opposite, uint32_t cross_ticks)
|
||||
{
|
||||
update_child(Price(limit), Quantity(this_state().quantity()));
|
||||
detach();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const bts::orderman::BookSide& buybook = book.book.side(api::Side::SIDE_BUY);
|
||||
for(const auto& i : buybook)
|
||||
{
|
||||
me->opposite_at_inception = i;
|
||||
if(me->opposite_at_inception.price() < new_price)
|
||||
break;
|
||||
}
|
||||
if(me->opposite_at_inception.price() >= new_price)
|
||||
detach();
|
||||
me->num_chases += cross_ticks+1;
|
||||
if(me->num_chases >= me->max_chase_ticks)
|
||||
detach();
|
||||
report_state();
|
||||
}
|
||||
|
||||
void LeanOrder::choose_trade_toward_behavior(const md::PriceSize& opposite)
|
||||
{
|
||||
switch (me->trade_toward_behavior)
|
||||
{
|
||||
case LeanOrderDefinition_OnToward_CANCEL:
|
||||
{
|
||||
cancel_child();
|
||||
transition(ORDER_STATUS_CANCELED);
|
||||
report_state();
|
||||
}
|
||||
break;
|
||||
case LeanOrderDefinition_OnToward_DETACH:
|
||||
{
|
||||
detach();
|
||||
}
|
||||
break;
|
||||
case LeanOrderDefinition_OnToward_REPRICE:
|
||||
{
|
||||
me->opposite_at_inception = opposite;
|
||||
}
|
||||
break;
|
||||
case LeanOrderDefinition_OnToward_NO_CHANGE:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void LeanOrder::on_book_update(const bts::orderman::BookUpdateEvent& book)
|
||||
{
|
||||
@@ -91,26 +149,26 @@ void LeanOrder::on_book_update(const bts::orderman::BookUpdateEvent& book)
|
||||
{
|
||||
const auto& opposite = book.book.side(api::Side::SIDE_SELL)[0];
|
||||
if(opposite.price() < me->opposite_at_inception.price())
|
||||
me->opposite_at_inception = opposite; //your lucky day, try to get luckier
|
||||
choose_trade_toward_behavior(opposite);
|
||||
else if(opposite.price() > me->opposite_at_inception.price())
|
||||
payup(opposite); //you missed, payup
|
||||
payup(opposite, book); //you missed, payup/join
|
||||
else
|
||||
{
|
||||
if(opposite.size() <= thresh.value())
|
||||
payup(opposite);
|
||||
payup(opposite, book);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto& opposite = book.book.side(api::Side::SIDE_BUY)[0];
|
||||
if(opposite.price() > me->opposite_at_inception.price())
|
||||
me->opposite_at_inception = opposite;
|
||||
choose_trade_toward_behavior(opposite);
|
||||
else if(opposite.price() < me->opposite_at_inception.price())
|
||||
paydown(opposite);
|
||||
paydown(opposite, book);
|
||||
else
|
||||
{
|
||||
if(opposite.size() <= thresh.value())
|
||||
paydown(opposite);
|
||||
paydown(opposite, book);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,8 +24,9 @@ protected:
|
||||
api::orderman::OrderState&, api::orderman::OrderDefinition*) override;
|
||||
void on_book_update(const bts::orderman::BookUpdateEvent&) override;
|
||||
private:
|
||||
void payup(const md::PriceSize& opposite, uint32_t cross_ticks = 0) override;
|
||||
void paydown(const md::PriceSize& opposite, uint32_t cross_ticks = 0) override;
|
||||
void payup(const md::PriceSize& opposite, const bts::orderman::BookUpdateEvent&, uint32_t cross_ticks = 0) override;
|
||||
void paydown(const md::PriceSize& opposite, const bts::orderman::BookUpdateEvent&, uint32_t cross_ticks = 0) override;
|
||||
void choose_trade_toward_behavior(const md::PriceSize& opposite);
|
||||
|
||||
private:
|
||||
struct PImpl;
|
||||
|
||||
@@ -43,7 +43,7 @@ void TickOrder::setup_child(const api::orderman::OrderDefinition& this_def, cons
|
||||
me->detach_on_toward = tick_def.detach_on_toward();
|
||||
}
|
||||
|
||||
void TickOrder::payup(const md::PriceSize& opposite, uint32_t cross_ticks)
|
||||
void TickOrder::payup(const md::PriceSize& opposite, const bts::orderman::BookUpdateEvent&, uint32_t cross_ticks)
|
||||
{
|
||||
double new_price = opposite.price() + tick_size() * cross_ticks;
|
||||
double limit = me->opposite_at_inception.price() + tick_size() * me->max_chase_ticks;
|
||||
@@ -57,7 +57,7 @@ void TickOrder::payup(const md::PriceSize& opposite, uint32_t cross_ticks)
|
||||
report_state();
|
||||
}
|
||||
|
||||
void TickOrder::paydown(const md::PriceSize& opposite, uint32_t cross_ticks)
|
||||
void TickOrder::paydown(const md::PriceSize& opposite, const bts::orderman::BookUpdateEvent&, uint32_t cross_ticks)
|
||||
{
|
||||
double new_price = opposite.price() - tick_size() * cross_ticks;
|
||||
double limit = me->opposite_at_inception.price() - tick_size() * me->max_chase_ticks;
|
||||
@@ -127,11 +127,11 @@ void TickOrder::on_book_update(const bts::orderman::BookUpdateEvent& book)
|
||||
me->opposite_at_inception = opposite;
|
||||
}
|
||||
else if(opposite.price() > me->opposite_at_inception.price())
|
||||
payup(opposite, me->cross_ticks);
|
||||
payup(opposite, book, me->cross_ticks);
|
||||
else
|
||||
{
|
||||
if(opposite.size() <= thresh.value())
|
||||
payup(opposite, me->cross_ticks);
|
||||
payup(opposite, book, me->cross_ticks);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -145,11 +145,11 @@ void TickOrder::on_book_update(const bts::orderman::BookUpdateEvent& book)
|
||||
me->opposite_at_inception = opposite;
|
||||
}
|
||||
else if(opposite.price() < me->opposite_at_inception.price())
|
||||
paydown(opposite, me->cross_ticks);
|
||||
paydown(opposite, book, me->cross_ticks);
|
||||
else
|
||||
{
|
||||
if(opposite.size() <= thresh.value())
|
||||
paydown(opposite, me->cross_ticks);
|
||||
paydown(opposite, book, me->cross_ticks);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,8 +24,8 @@ protected:
|
||||
api::orderman::OrderState&, api::orderman::OrderDefinition*) override;
|
||||
void on_book_update(const bts::orderman::BookUpdateEvent&) override;
|
||||
private:
|
||||
void payup(const md::PriceSize& opposite, uint32_t cross_ticks) override;
|
||||
void paydown(const md::PriceSize& opposite, uint32_t cross_ticks) override;
|
||||
void payup(const md::PriceSize& opposite, const bts::orderman::BookUpdateEvent&, uint32_t cross_ticks) override;
|
||||
void paydown(const md::PriceSize& opposite, const bts::orderman::BookUpdateEvent&, uint32_t cross_ticks) override;
|
||||
|
||||
private:
|
||||
struct PImpl;
|
||||
|
||||
@@ -80,6 +80,33 @@ void TopOrder::on_book_update(const bts::orderman::BookUpdateEvent& book)
|
||||
{
|
||||
case ORDER_STATUS_INACTIVE:
|
||||
{
|
||||
Quantity thresh = SpreaderUtils::_quantity_threshold(me->top_threshold, Quantity(this_state().quantity()));
|
||||
if(definition().side() == api::SIDE_BUY)
|
||||
{
|
||||
const auto& same = book.book.side(api::Side::SIDE_BUY)[0];
|
||||
if(securities::test_price_equality(this_state().price(), same.price(), tick_size()))
|
||||
{
|
||||
if(same.size() <= thresh.value())
|
||||
{
|
||||
transition(ORDER_STATUS_CANCELED);
|
||||
report_state();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto& same = book.book.side(api::Side::SIDE_SELL)[0];
|
||||
if(securities::test_price_equality(this_state().price(), same.price(), tick_size()))
|
||||
{
|
||||
if(same.size() <= thresh.value())
|
||||
{
|
||||
transition(ORDER_STATUS_CANCELED);
|
||||
report_state();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
activate_child(Price(this_state().price()), Quantity(this_state().quantity()));
|
||||
transition(ORDER_STATUS_WORKING);
|
||||
report_state();
|
||||
|
||||
@@ -12,6 +12,7 @@ def gen_create_table(d,t):
|
||||
print ".import %s trace" % t
|
||||
print '''create view tick_to_order as select
|
||||
event_id,
|
||||
order_key,
|
||||
(fix_socket_w - md) / 1000 total,
|
||||
(md_src-md)/1000 md_to_md_src,
|
||||
(spreader_on_book-md_src)/1000 md_src_to_spreader,
|
||||
|
||||
+446
-434
@@ -1,434 +1,446 @@
|
||||
#include "datetime.h"
|
||||
|
||||
#include "bts_assert.h"
|
||||
#include "TimeStamp.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||
#include <boost/date_time/gregorian/gregorian.hpp>
|
||||
|
||||
namespace bts {
|
||||
namespace datetime {
|
||||
|
||||
const boost::gregorian::date anchor (1970, boost::gregorian::Jan, 1);
|
||||
|
||||
unsigned long long ptime_to_ll (PTime const &t)
|
||||
{
|
||||
long long days = (t.date () - anchor).days ();
|
||||
long long usecs = t.time_of_day ().total_microseconds ();
|
||||
return (days << 40) + usecs;
|
||||
}
|
||||
|
||||
LLDateTime ll_now ()
|
||||
{
|
||||
return ptime_to_ll (boost::posix_time::microsec_clock::universal_time ());
|
||||
}
|
||||
|
||||
LLDateTime ll_local_now ()
|
||||
{
|
||||
return ptime_to_ll (boost::posix_time::microsec_clock::local_time ());
|
||||
}
|
||||
|
||||
long unix_now() {
|
||||
return (boost::posix_time::microsec_clock::universal_time() - PTime(anchor)).total_microseconds() / (long)1E6;
|
||||
}
|
||||
|
||||
long long ll_diff_in_us (unsigned long long ll1, unsigned long long ll2)
|
||||
{
|
||||
#ifndef NO_CPP0X
|
||||
auto p1 = ll_to_ptime (ll1);
|
||||
auto p2 = ll_to_ptime (ll2); // NOLINT
|
||||
return (p2 - p1).total_microseconds ();
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
long long ptime_diff_in_us (PTime const &t1, PTime const &t2)
|
||||
{
|
||||
return (t1 - t2).total_microseconds ();
|
||||
}
|
||||
|
||||
long long ptime_diff_in_ms (PTime const &t1, PTime const &t2)
|
||||
{
|
||||
return (t1 - t2).total_milliseconds ();
|
||||
}
|
||||
|
||||
long long ptime_diff_in_secs (PTime const &t1, PTime const &t2)
|
||||
{
|
||||
return (t1 - t2).total_seconds ();
|
||||
}
|
||||
|
||||
PTime timestamp_to_ptime(utils::TimeStamp const&t)
|
||||
{
|
||||
auto now_ts = utils::TimeStamp::now();
|
||||
auto now = datetime::now();
|
||||
|
||||
auto delta = (now_ts - t).milliseconds();
|
||||
return now - boost::posix_time::milliseconds(delta);
|
||||
}
|
||||
|
||||
utils::TimeStamp ptime_to_timestamp(PTime const &t)
|
||||
{
|
||||
auto now_ts = utils::TimeStamp::now();
|
||||
auto now = datetime::now();
|
||||
|
||||
auto delta = (now - t).total_milliseconds();
|
||||
return now_ts.shift_ms (delta);
|
||||
}
|
||||
|
||||
long long usecs_since (PTime const &t1)
|
||||
{
|
||||
return (boost::posix_time::microsec_clock::universal_time () - t1).total_microseconds ();
|
||||
}
|
||||
|
||||
long long msecs_since (PTime const &t1)
|
||||
{
|
||||
return (boost::posix_time::microsec_clock::universal_time () - t1).total_milliseconds ();
|
||||
}
|
||||
|
||||
PTime now ()
|
||||
{
|
||||
return boost::posix_time::microsec_clock::universal_time ();
|
||||
}
|
||||
|
||||
PTime local_now ()
|
||||
{
|
||||
return boost::posix_time::microsec_clock::local_time ();
|
||||
}
|
||||
|
||||
PTime make_ptime (int year, int month, int day)
|
||||
{
|
||||
// January=1
|
||||
bts_assert_d(month > 0);
|
||||
boost::gregorian::date dt (year, boost::gregorian::greg_month (month),day);
|
||||
return PTime (dt);
|
||||
}
|
||||
|
||||
PTime make_ptime (int year, int month, int day, int hour, int minute, int second, int millisecond)
|
||||
{
|
||||
boost::gregorian::date dt (year, boost::gregorian::greg_month (month),day);
|
||||
return PTime (dt, boost::posix_time::time_duration(hour, minute, second) + boost::posix_time::milliseconds(millisecond));
|
||||
}
|
||||
|
||||
PTime ll_to_ptime (LLDateTime ll)
|
||||
{
|
||||
boost::gregorian::date_duration days (ll >> 40);
|
||||
boost::posix_time::time_duration usecs (boost::posix_time::microseconds (0x000000ffffffffff & ll));
|
||||
return PTime (anchor + days,usecs);
|
||||
}
|
||||
|
||||
extern "C" BTS_EXPORT int ll_to_cstr (LLDateTime ll, char*buffer, int buffer_sz)
|
||||
{
|
||||
const int str_sz = sizeof ("20121204-14:44:02.000");
|
||||
bts_assert (buffer_sz >= str_sz);
|
||||
auto str = FIX_timestamp (ll_to_ptime (ll));
|
||||
bts_assert (str.size () == str_sz-1);
|
||||
memcpy (buffer,&str[0],str.size ());
|
||||
buffer[str.size ()] = '\0';
|
||||
return str.size ();
|
||||
}
|
||||
|
||||
std::string time_string (PTime const &t)
|
||||
{
|
||||
int hours = t.time_of_day ().hours ();
|
||||
int minutes = t.time_of_day ().minutes ();
|
||||
int seconds = t.time_of_day ().seconds ();
|
||||
int whole_ms = seconds * 1000 + minutes * 1000 * 60 + hours * 1000 * 60 * 60;
|
||||
int frac_ms = t.time_of_day ().total_milliseconds () - whole_ms;
|
||||
std::ostringstream out;
|
||||
out << std::setw( 2 ) << std::setfill( '0' ) << hours << ":"
|
||||
<< std::setw( 2 ) << std::setfill( '0' ) << minutes << ":"
|
||||
<< std::setw( 2 ) << std::setfill( '0' ) << seconds << "."
|
||||
<< std::setw( 3 ) << std::setfill( '0' ) << frac_ms;
|
||||
return out.str ();
|
||||
}
|
||||
|
||||
std::string date_string (PTime const &t)
|
||||
{
|
||||
std::string result (10,' ');
|
||||
sprintf (&result[0], "%02d/%02d/%04d",
|
||||
t.date ().month ().as_number (),
|
||||
t.date ().day ().as_number (),
|
||||
(unsigned short) t.date ().year ());
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string iso_date_time_string (PTime const &t)
|
||||
{
|
||||
return boost::posix_time::to_iso_extended_string(t);
|
||||
}
|
||||
|
||||
std::string simple_date_time_string (PTime const &t)
|
||||
{
|
||||
return boost::posix_time::to_simple_string(t);
|
||||
}
|
||||
|
||||
std::string FIX_date_time_string (PTime const &t)
|
||||
{
|
||||
std::string result (strlen ("20121204-14:44:02"),' ');
|
||||
sprintf (&result[0],"%d%02d%02d-%02d:%02d:%02d",
|
||||
(unsigned short) t.date ().year (),
|
||||
t.date ().month ().as_number (),
|
||||
t.date ().day ().as_number (),
|
||||
t.time_of_day ().hours (),
|
||||
t.time_of_day ().minutes (),
|
||||
t.time_of_day ().seconds ());
|
||||
return result;
|
||||
}
|
||||
|
||||
void FIX_timestamp (PTime const &t, std::string *out)
|
||||
{
|
||||
int hours = t.time_of_day ().hours ();
|
||||
int minutes = t.time_of_day ().minutes ();
|
||||
int seconds = t.time_of_day ().seconds ();
|
||||
int whole_ms = seconds * 1000 + minutes * 1000 * 60 + hours * 1000 * 60 * 60;
|
||||
int frac_ms = t.time_of_day ().total_milliseconds () - whole_ms;
|
||||
out->resize(sizeof ("20121204-14:44:02.000"));
|
||||
|
||||
#ifdef WIN32
|
||||
_snprintf (&(*out)[0],out->size (),
|
||||
"%04d%02d%02d-%02d:%02d:%02d.%03d",
|
||||
(int) t.date ().year (),
|
||||
t.date ().month ().as_number (),
|
||||
t.date ().day ().as_number (),
|
||||
hours,
|
||||
minutes,
|
||||
seconds,
|
||||
frac_ms);
|
||||
#else
|
||||
snprintf (&(*out)[0],out->size (),
|
||||
"%04d%02d%02d-%02d:%02d:%02d.%03d",
|
||||
(int) t.date ().year (),
|
||||
t.date ().month ().as_number (),
|
||||
t.date ().day ().as_number (),
|
||||
hours,
|
||||
minutes,
|
||||
seconds,
|
||||
frac_ms);
|
||||
#endif
|
||||
out->resize (out->size ()-1);
|
||||
}
|
||||
|
||||
std::string FIX_timestamp (PTime const &t)
|
||||
{
|
||||
std::string result;
|
||||
FIX_timestamp(t,&result);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool is_from_today(PTime const &time)
|
||||
{
|
||||
return (time.date() == boost::posix_time::microsec_clock::universal_time().date());
|
||||
}
|
||||
|
||||
bool is_at_least_msecs_old (PTime const &time, uint64_t ms)
|
||||
{
|
||||
PTime now = boost::posix_time::microsec_clock::universal_time ();
|
||||
return (now - time).total_milliseconds () >= (int64_t) ms;
|
||||
}
|
||||
|
||||
bool is_at_least_secs_old (PTime const &time, uint64_t sec)
|
||||
{
|
||||
PTime now = boost::posix_time::microsec_clock::universal_time ();
|
||||
return (now - time).total_seconds () >= (int64_t) sec;
|
||||
}
|
||||
|
||||
bool is_at_least_mins_old (PTime const &time, uint64_t mins)
|
||||
{
|
||||
PTime now = boost::posix_time::microsec_clock::universal_time ();
|
||||
return ((now - time).total_seconds ()/60) >= (int64_t) mins;
|
||||
}
|
||||
|
||||
BTS_EXPORT bool is_at_least_hours_old (PTime const &time, uint64_t hours)
|
||||
{
|
||||
PTime now = boost::posix_time::microsec_clock::universal_time ();
|
||||
int64_t secs = (int64_t) ((now - time).total_seconds ());
|
||||
return (secs/60/60) >= (int64_t) hours;
|
||||
}
|
||||
|
||||
BTS_EXPORT bool is_at_least_days_old (PTime const &time, uint64_t days)
|
||||
{
|
||||
return is_at_least_hours_old (time, days *24);
|
||||
}
|
||||
|
||||
BTS_EXPORT LLDateTime ll_from_unix_epoch(long seconds)
|
||||
{
|
||||
PTime result(anchor, boost::posix_time::seconds(seconds));
|
||||
return ptime_to_ll(result);
|
||||
}
|
||||
|
||||
BTS_EXPORT LLDateTime ll_from_unix_epoch_ms(long long milliseconds)
|
||||
{
|
||||
PTime result(anchor, boost::posix_time::millisec(milliseconds));
|
||||
return ptime_to_ll(result);
|
||||
}
|
||||
|
||||
BTS_EXPORT void ll_to_parts(LLDateTime ll_time, int& year, int& month, int& day, int& hour, int& minute, int& second, int& millisecond)
|
||||
{
|
||||
PTime t = ll_to_ptime(ll_time);
|
||||
year = t.date().year();
|
||||
month = t.date().month();
|
||||
day = t.date().day();
|
||||
hour = t.time_of_day().hours();
|
||||
minute = t.time_of_day().minutes();
|
||||
second = t.time_of_day().seconds();
|
||||
int whole_ms = second * 1000 + minute * 1000 * 60 + hour * 1000 * 60 * 60;
|
||||
millisecond = t.time_of_day().total_milliseconds() - whole_ms;
|
||||
}
|
||||
|
||||
BTS_EXPORT LLDateTime ll_from_parts(int year, int month, int day, int hour, int minute, int second, int millisecond)
|
||||
{
|
||||
return ptime_to_ll(make_ptime(year, month, day, hour, minute, second, millisecond)); // NOLINT
|
||||
}
|
||||
|
||||
BTS_EXPORT LLDateTime ll_from_date(const Date& date) {
|
||||
if (date.year == 0 && date.day == 0 && date.month == 0) {
|
||||
return 0;
|
||||
}
|
||||
return ll_from_parts(date.year, date.month, date.day, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
Date ll_to_date(LLDateTime time) {
|
||||
Date date;
|
||||
int year, month, day, unused;
|
||||
ll_to_parts(time, year, month, day, unused, unused, unused, unused);
|
||||
date.year = year;
|
||||
date.month = month;
|
||||
date.day = day;
|
||||
return date;
|
||||
}
|
||||
|
||||
struct UtcTimeStamp {
|
||||
int hour;
|
||||
int min;
|
||||
int sec;
|
||||
int msec;
|
||||
int day;
|
||||
int month;
|
||||
int year;
|
||||
};
|
||||
|
||||
struct UtcTimeStampConvertor
|
||||
{
|
||||
static UtcTimeStamp convert( const std::string& value)
|
||||
{
|
||||
bool haveMilliseconds = false;
|
||||
|
||||
switch( value.size() )
|
||||
{
|
||||
case 21: haveMilliseconds = true;
|
||||
case 17: break;
|
||||
default: bts_assert(false);
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
int c = 0;
|
||||
for( c = 0; c < 8; ++c )
|
||||
if( !isdigit(value[i++]) ) bts_assert(false);
|
||||
if (value[i++] != '-') bts_assert(false);
|
||||
for( c = 0; c < 2; ++c )
|
||||
if( !isdigit(value[i++]) ) bts_assert(false);
|
||||
if( value[i++] != ':' ) bts_assert(false);
|
||||
for( c = 0; c < 2; ++c )
|
||||
if( !isdigit(value[i++]) ) bts_assert(false);
|
||||
if( value[i++] != ':' ) bts_assert(false);
|
||||
for( c = 0; c < 2; ++c )
|
||||
if( !isdigit(value[i++]) ) bts_assert(false);
|
||||
|
||||
if( haveMilliseconds )
|
||||
{
|
||||
if( value[i++] != '.' ) bts_assert(false);
|
||||
for( c = 0; c < 3; ++c )
|
||||
if( !isdigit(value[i++]) ) bts_assert(false);
|
||||
}
|
||||
|
||||
int year, mon, mday, hour, min, sec, millis;
|
||||
|
||||
i = 0;
|
||||
|
||||
year = value[i++] - '0';
|
||||
year = 10 * year + value[i++] - '0';
|
||||
year = 10 * year + value[i++] - '0';
|
||||
year = 10 * year + value[i++] - '0';
|
||||
|
||||
mon = value[i++] - '0';
|
||||
mon = 10 * mon + value[i++] - '0';
|
||||
if( mon < 1 || 12 < mon ) bts_assert(false);
|
||||
|
||||
mday = value[i++] - '0';
|
||||
mday = 10 * mday + value[i++] - '0';
|
||||
if( mday < 1 || 31 < mday ) bts_assert(false);
|
||||
|
||||
++i; // skip '-'
|
||||
|
||||
hour = value[i++] - '0';
|
||||
hour = 10 * hour + value[i++] - '0';
|
||||
// No check for >= 0 as no '-' are converted here
|
||||
if( 23 < hour ) bts_assert(false);
|
||||
|
||||
++i; // skip ':'
|
||||
|
||||
min = value[i++] - '0';
|
||||
min = 10 * min + value[i++] - '0';
|
||||
// No check for >= 0 as no '-' are converted here
|
||||
if( 59 < min ) bts_assert(false);
|
||||
|
||||
++i; // skip ':'
|
||||
|
||||
sec = value[i++] - '0';
|
||||
sec = 10 * sec + value[i++] - '0';
|
||||
|
||||
// No check for >= 0 as no '-' are converted here
|
||||
if( 60 < sec ) bts_assert(false);
|
||||
|
||||
if( haveMilliseconds )
|
||||
{
|
||||
millis = (100 * (value[i+1] - '0')
|
||||
+ 10 * (value[i+2] - '0')
|
||||
+ (value[i+3] - '0'));
|
||||
}
|
||||
else
|
||||
millis = 0;
|
||||
|
||||
return UtcTimeStamp {hour, min, sec, millis, mday, mon, year};
|
||||
}
|
||||
};
|
||||
|
||||
PTime parse_FIX_timestamp (const std::string &ts_str)
|
||||
{
|
||||
auto ts = UtcTimeStampConvertor::convert (ts_str);
|
||||
boost::gregorian::date dt(ts.year, ts.month, ts.day);
|
||||
// fucking boost. unbelieavable.
|
||||
auto ticks_per_second = boost::posix_time::time_duration::rep_type::res_adjust();
|
||||
boost::posix_time::time_duration td (ts.hour, ts.min, ts.sec, ts.msec * (ticks_per_second / 1000));
|
||||
return PTime(dt, td);
|
||||
}
|
||||
|
||||
#ifndef NO_CPP0X
|
||||
extern "C" BTS_EXPORT LLDateTime ll_today() {
|
||||
return datetime::ll_from_date(today());
|
||||
}
|
||||
|
||||
BTS_EXPORT Date today() {
|
||||
auto now = datetime::now();
|
||||
Date date;
|
||||
date.year = now.date().year();
|
||||
date.month = now.date().month();
|
||||
date.day = now.date().day();
|
||||
return date;
|
||||
}
|
||||
BTS_EXPORT Date add_days(Date indate, int days) {
|
||||
boost::gregorian::date dt(indate.year, indate.month, indate.day);
|
||||
auto adjusted_dt = dt + boost::gregorian::days(days);
|
||||
Date date(adjusted_dt.year(), adjusted_dt.month(), adjusted_dt.day());
|
||||
return date;
|
||||
}
|
||||
|
||||
extern "C" BTS_EXPORT LLDateTime ll_yesterday() {
|
||||
auto now = datetime::now() - boost::gregorian::days(1);
|
||||
return datetime::ll_from_parts(now.date().year(), now.date().month(), now.date().day(), 0, 0, 0, 0);
|
||||
}
|
||||
#endif
|
||||
}}
|
||||
#include "datetime.h"
|
||||
|
||||
#include "bts_assert.h"
|
||||
#include "TimeStamp.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||
#include <boost/date_time/gregorian/gregorian.hpp>
|
||||
|
||||
namespace bts {
|
||||
namespace datetime {
|
||||
|
||||
const boost::gregorian::date anchor (1970, boost::gregorian::Jan, 1);
|
||||
const uint64_t anchor_ticks = 621355968000000000;
|
||||
const uint64_t micros_per_day = 86400000000;
|
||||
const uint64_t ticks_per_micro = 10;
|
||||
const uint64_t ticks_per_day = micros_per_day * ticks_per_micro;
|
||||
const uint64_t ticks_mask = 0x3FFFFFFFFFFFFFFF;
|
||||
// DateTimeKinds
|
||||
const uint64_t KindUnspecified = 0x0000000000000000;
|
||||
const uint64_t KindUtc = 0x4000000000000000;
|
||||
const uint64_t KindLocal = 0x8000000000000000;
|
||||
|
||||
unsigned long long ptime_to_ll(PTime const &t, uint64_t const &kind) {
|
||||
uint64_t days = (t.date() - anchor).days();
|
||||
uint64_t usecs = t.time_of_day().total_microseconds();
|
||||
uint64_t ticks = anchor_ticks + usecs * ticks_per_micro + days * ticks_per_day;
|
||||
return (ticks & ticks_mask) | kind;
|
||||
}
|
||||
|
||||
unsigned long long ptime_to_ll(PTime const &t) {
|
||||
return ptime_to_ll(t, KindUnspecified);
|
||||
}
|
||||
|
||||
LLDateTime ll_now() {
|
||||
return ptime_to_ll(boost::posix_time::microsec_clock::universal_time(), KindUtc);
|
||||
}
|
||||
|
||||
LLDateTime ll_local_now() {
|
||||
return ptime_to_ll(boost::posix_time::microsec_clock::local_time (), KindLocal);
|
||||
}
|
||||
|
||||
long unix_now() {
|
||||
return (boost::posix_time::microsec_clock::universal_time() - PTime(anchor)).total_microseconds() / (long)1E6;
|
||||
}
|
||||
|
||||
long long ll_diff_in_us (unsigned long long ll1, unsigned long long ll2)
|
||||
{
|
||||
#ifndef NO_CPP0X
|
||||
auto p1 = ll_to_ptime (ll1);
|
||||
auto p2 = ll_to_ptime (ll2); // NOLINT
|
||||
return (p2 - p1).total_microseconds ();
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
long long ptime_diff_in_us (PTime const &t1, PTime const &t2)
|
||||
{
|
||||
return (t1 - t2).total_microseconds ();
|
||||
}
|
||||
|
||||
long long ptime_diff_in_ms (PTime const &t1, PTime const &t2)
|
||||
{
|
||||
return (t1 - t2).total_milliseconds ();
|
||||
}
|
||||
|
||||
long long ptime_diff_in_secs (PTime const &t1, PTime const &t2)
|
||||
{
|
||||
return (t1 - t2).total_seconds ();
|
||||
}
|
||||
|
||||
PTime timestamp_to_ptime(utils::TimeStamp const&t)
|
||||
{
|
||||
auto now_ts = utils::TimeStamp::now();
|
||||
auto now = datetime::now();
|
||||
|
||||
auto delta = (now_ts - t).milliseconds();
|
||||
return now - boost::posix_time::milliseconds(delta);
|
||||
}
|
||||
|
||||
utils::TimeStamp ptime_to_timestamp(PTime const &t)
|
||||
{
|
||||
auto now_ts = utils::TimeStamp::now();
|
||||
auto now = datetime::now();
|
||||
|
||||
auto delta = (now - t).total_milliseconds();
|
||||
return now_ts.shift_ms (delta);
|
||||
}
|
||||
|
||||
long long usecs_since (PTime const &t1)
|
||||
{
|
||||
return (boost::posix_time::microsec_clock::universal_time () - t1).total_microseconds ();
|
||||
}
|
||||
|
||||
long long msecs_since (PTime const &t1)
|
||||
{
|
||||
return (boost::posix_time::microsec_clock::universal_time () - t1).total_milliseconds ();
|
||||
}
|
||||
|
||||
PTime now ()
|
||||
{
|
||||
return boost::posix_time::microsec_clock::universal_time ();
|
||||
}
|
||||
|
||||
PTime local_now ()
|
||||
{
|
||||
return boost::posix_time::microsec_clock::local_time ();
|
||||
}
|
||||
|
||||
PTime make_ptime (int year, int month, int day)
|
||||
{
|
||||
// January=1
|
||||
bts_assert_d(month > 0);
|
||||
boost::gregorian::date dt (year, boost::gregorian::greg_month (month),day);
|
||||
return PTime (dt);
|
||||
}
|
||||
|
||||
PTime make_ptime (int year, int month, int day, int hour, int minute, int second, int millisecond)
|
||||
{
|
||||
boost::gregorian::date dt (year, boost::gregorian::greg_month (month),day);
|
||||
return PTime (dt, boost::posix_time::time_duration(hour, minute, second) + boost::posix_time::milliseconds(millisecond));
|
||||
}
|
||||
|
||||
PTime ll_to_ptime (LLDateTime ll) {
|
||||
ll &= ticks_mask;
|
||||
uint64_t ticks_part = ll % ticks_per_day;
|
||||
uint64_t days_part = (ll - anchor_ticks) / ticks_per_day;
|
||||
return PTime(anchor + boost::gregorian::date_duration(days_part),
|
||||
boost::posix_time::time_duration(boost::posix_time::microseconds(ticks_part / ticks_per_micro)));
|
||||
}
|
||||
|
||||
extern "C" BTS_EXPORT int ll_to_cstr (LLDateTime ll, char*buffer, int buffer_sz)
|
||||
{
|
||||
const int str_sz = sizeof ("20121204-14:44:02.000");
|
||||
bts_assert (buffer_sz >= str_sz);
|
||||
auto str = FIX_timestamp (ll_to_ptime (ll));
|
||||
bts_assert (str.size () == str_sz-1);
|
||||
memcpy (buffer,&str[0],str.size ());
|
||||
buffer[str.size ()] = '\0';
|
||||
return str.size ();
|
||||
}
|
||||
|
||||
std::string time_string (PTime const &t)
|
||||
{
|
||||
int hours = t.time_of_day ().hours ();
|
||||
int minutes = t.time_of_day ().minutes ();
|
||||
int seconds = t.time_of_day ().seconds ();
|
||||
int whole_ms = seconds * 1000 + minutes * 1000 * 60 + hours * 1000 * 60 * 60;
|
||||
int frac_ms = t.time_of_day ().total_milliseconds () - whole_ms;
|
||||
std::ostringstream out;
|
||||
out << std::setw( 2 ) << std::setfill( '0' ) << hours << ":"
|
||||
<< std::setw( 2 ) << std::setfill( '0' ) << minutes << ":"
|
||||
<< std::setw( 2 ) << std::setfill( '0' ) << seconds << "."
|
||||
<< std::setw( 3 ) << std::setfill( '0' ) << frac_ms;
|
||||
return out.str ();
|
||||
}
|
||||
|
||||
std::string date_string (PTime const &t)
|
||||
{
|
||||
std::string result (10,' ');
|
||||
sprintf (&result[0], "%02d/%02d/%04d",
|
||||
t.date ().month ().as_number (),
|
||||
t.date ().day ().as_number (),
|
||||
(unsigned short) t.date ().year ());
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string iso_date_time_string (PTime const &t)
|
||||
{
|
||||
return boost::posix_time::to_iso_extended_string(t);
|
||||
}
|
||||
|
||||
std::string simple_date_time_string (PTime const &t)
|
||||
{
|
||||
return boost::posix_time::to_simple_string(t);
|
||||
}
|
||||
|
||||
std::string FIX_date_time_string (PTime const &t)
|
||||
{
|
||||
std::string result (strlen ("20121204-14:44:02"),' ');
|
||||
sprintf (&result[0],"%d%02d%02d-%02d:%02d:%02d",
|
||||
(unsigned short) t.date ().year (),
|
||||
t.date ().month ().as_number (),
|
||||
t.date ().day ().as_number (),
|
||||
t.time_of_day ().hours (),
|
||||
t.time_of_day ().minutes (),
|
||||
t.time_of_day ().seconds ());
|
||||
return result;
|
||||
}
|
||||
|
||||
void FIX_timestamp (PTime const &t, std::string *out)
|
||||
{
|
||||
int hours = t.time_of_day ().hours ();
|
||||
int minutes = t.time_of_day ().minutes ();
|
||||
int seconds = t.time_of_day ().seconds ();
|
||||
int whole_ms = seconds * 1000 + minutes * 1000 * 60 + hours * 1000 * 60 * 60;
|
||||
int frac_ms = t.time_of_day ().total_milliseconds () - whole_ms;
|
||||
out->resize(sizeof ("20121204-14:44:02.000"));
|
||||
|
||||
#ifdef WIN32
|
||||
_snprintf (&(*out)[0],out->size (),
|
||||
"%04d%02d%02d-%02d:%02d:%02d.%03d",
|
||||
(int) t.date ().year (),
|
||||
t.date ().month ().as_number (),
|
||||
t.date ().day ().as_number (),
|
||||
hours,
|
||||
minutes,
|
||||
seconds,
|
||||
frac_ms);
|
||||
#else
|
||||
snprintf (&(*out)[0],out->size (),
|
||||
"%04d%02d%02d-%02d:%02d:%02d.%03d",
|
||||
(int) t.date ().year (),
|
||||
t.date ().month ().as_number (),
|
||||
t.date ().day ().as_number (),
|
||||
hours,
|
||||
minutes,
|
||||
seconds,
|
||||
frac_ms);
|
||||
#endif
|
||||
out->resize (out->size ()-1);
|
||||
}
|
||||
|
||||
std::string FIX_timestamp (PTime const &t)
|
||||
{
|
||||
std::string result;
|
||||
FIX_timestamp(t,&result);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool is_from_today(PTime const &time)
|
||||
{
|
||||
return (time.date() == boost::posix_time::microsec_clock::universal_time().date());
|
||||
}
|
||||
|
||||
bool is_at_least_msecs_old (PTime const &time, uint64_t ms)
|
||||
{
|
||||
PTime now = boost::posix_time::microsec_clock::universal_time ();
|
||||
return (now - time).total_milliseconds () >= (int64_t) ms;
|
||||
}
|
||||
|
||||
bool is_at_least_secs_old (PTime const &time, uint64_t sec)
|
||||
{
|
||||
PTime now = boost::posix_time::microsec_clock::universal_time ();
|
||||
return (now - time).total_seconds () >= (int64_t) sec;
|
||||
}
|
||||
|
||||
bool is_at_least_mins_old (PTime const &time, uint64_t mins)
|
||||
{
|
||||
PTime now = boost::posix_time::microsec_clock::universal_time ();
|
||||
return ((now - time).total_seconds ()/60) >= (int64_t) mins;
|
||||
}
|
||||
|
||||
BTS_EXPORT bool is_at_least_hours_old (PTime const &time, uint64_t hours)
|
||||
{
|
||||
PTime now = boost::posix_time::microsec_clock::universal_time ();
|
||||
int64_t secs = (int64_t) ((now - time).total_seconds ());
|
||||
return (secs/60/60) >= (int64_t) hours;
|
||||
}
|
||||
|
||||
BTS_EXPORT bool is_at_least_days_old (PTime const &time, uint64_t days)
|
||||
{
|
||||
return is_at_least_hours_old (time, days *24);
|
||||
}
|
||||
|
||||
BTS_EXPORT LLDateTime ll_from_unix_epoch(long seconds)
|
||||
{
|
||||
PTime result(anchor, boost::posix_time::seconds(seconds));
|
||||
return ptime_to_ll(result, KindUnspecified);
|
||||
}
|
||||
|
||||
BTS_EXPORT LLDateTime ll_from_unix_epoch_ms(long long milliseconds)
|
||||
{
|
||||
PTime result(anchor, boost::posix_time::millisec(milliseconds));
|
||||
return ptime_to_ll(result, KindUnspecified);
|
||||
}
|
||||
|
||||
BTS_EXPORT void ll_to_parts(LLDateTime ll_time, int& year, int& month, int& day, int& hour, int& minute, int& second, int& millisecond)
|
||||
{
|
||||
PTime t = ll_to_ptime(ll_time);
|
||||
year = t.date().year();
|
||||
month = t.date().month();
|
||||
day = t.date().day();
|
||||
hour = t.time_of_day().hours();
|
||||
minute = t.time_of_day().minutes();
|
||||
second = t.time_of_day().seconds();
|
||||
int whole_ms = second * 1000 + minute * 1000 * 60 + hour * 1000 * 60 * 60;
|
||||
millisecond = t.time_of_day().total_milliseconds() - whole_ms;
|
||||
}
|
||||
|
||||
BTS_EXPORT LLDateTime ll_from_parts(int year, int month, int day, int hour, int minute, int second, int millisecond)
|
||||
{
|
||||
return ptime_to_ll(make_ptime(year, month, day, hour, minute, second, millisecond)); // NOLINT
|
||||
}
|
||||
|
||||
BTS_EXPORT LLDateTime ll_from_date(const Date& date) {
|
||||
if (date.year == 0 && date.day == 0 && date.month == 0) {
|
||||
return 0;
|
||||
}
|
||||
return ll_from_parts(date.year, date.month, date.day, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
Date ll_to_date(LLDateTime time) {
|
||||
Date date;
|
||||
int year, month, day, unused;
|
||||
ll_to_parts(time, year, month, day, unused, unused, unused, unused);
|
||||
date.year = year;
|
||||
date.month = month;
|
||||
date.day = day;
|
||||
return date;
|
||||
}
|
||||
|
||||
struct UtcTimeStamp {
|
||||
int hour;
|
||||
int min;
|
||||
int sec;
|
||||
int msec;
|
||||
int day;
|
||||
int month;
|
||||
int year;
|
||||
};
|
||||
|
||||
struct UtcTimeStampConvertor
|
||||
{
|
||||
static UtcTimeStamp convert( const std::string& value)
|
||||
{
|
||||
bool haveMilliseconds = false;
|
||||
|
||||
switch( value.size() )
|
||||
{
|
||||
case 21: haveMilliseconds = true;
|
||||
case 17: break;
|
||||
default: bts_assert(false);
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
int c = 0;
|
||||
for( c = 0; c < 8; ++c )
|
||||
if( !isdigit(value[i++]) ) bts_assert(false);
|
||||
if (value[i++] != '-') bts_assert(false);
|
||||
for( c = 0; c < 2; ++c )
|
||||
if( !isdigit(value[i++]) ) bts_assert(false);
|
||||
if( value[i++] != ':' ) bts_assert(false);
|
||||
for( c = 0; c < 2; ++c )
|
||||
if( !isdigit(value[i++]) ) bts_assert(false);
|
||||
if( value[i++] != ':' ) bts_assert(false);
|
||||
for( c = 0; c < 2; ++c )
|
||||
if( !isdigit(value[i++]) ) bts_assert(false);
|
||||
|
||||
if( haveMilliseconds )
|
||||
{
|
||||
if( value[i++] != '.' ) bts_assert(false);
|
||||
for( c = 0; c < 3; ++c )
|
||||
if( !isdigit(value[i++]) ) bts_assert(false);
|
||||
}
|
||||
|
||||
int year, mon, mday, hour, min, sec, millis;
|
||||
|
||||
i = 0;
|
||||
|
||||
year = value[i++] - '0';
|
||||
year = 10 * year + value[i++] - '0';
|
||||
year = 10 * year + value[i++] - '0';
|
||||
year = 10 * year + value[i++] - '0';
|
||||
|
||||
mon = value[i++] - '0';
|
||||
mon = 10 * mon + value[i++] - '0';
|
||||
if( mon < 1 || 12 < mon ) bts_assert(false);
|
||||
|
||||
mday = value[i++] - '0';
|
||||
mday = 10 * mday + value[i++] - '0';
|
||||
if( mday < 1 || 31 < mday ) bts_assert(false);
|
||||
|
||||
++i; // skip '-'
|
||||
|
||||
hour = value[i++] - '0';
|
||||
hour = 10 * hour + value[i++] - '0';
|
||||
// No check for >= 0 as no '-' are converted here
|
||||
if( 23 < hour ) bts_assert(false);
|
||||
|
||||
++i; // skip ':'
|
||||
|
||||
min = value[i++] - '0';
|
||||
min = 10 * min + value[i++] - '0';
|
||||
// No check for >= 0 as no '-' are converted here
|
||||
if( 59 < min ) bts_assert(false);
|
||||
|
||||
++i; // skip ':'
|
||||
|
||||
sec = value[i++] - '0';
|
||||
sec = 10 * sec + value[i++] - '0';
|
||||
|
||||
// No check for >= 0 as no '-' are converted here
|
||||
if( 60 < sec ) bts_assert(false);
|
||||
|
||||
if( haveMilliseconds )
|
||||
{
|
||||
millis = (100 * (value[i+1] - '0')
|
||||
+ 10 * (value[i+2] - '0')
|
||||
+ (value[i+3] - '0'));
|
||||
}
|
||||
else
|
||||
millis = 0;
|
||||
|
||||
return UtcTimeStamp {hour, min, sec, millis, mday, mon, year};
|
||||
}
|
||||
};
|
||||
|
||||
PTime parse_FIX_timestamp (const std::string &ts_str)
|
||||
{
|
||||
auto ts = UtcTimeStampConvertor::convert (ts_str);
|
||||
boost::gregorian::date dt(ts.year, ts.month, ts.day);
|
||||
// fucking boost. unbelieavable.
|
||||
auto ticks_per_second = boost::posix_time::time_duration::rep_type::res_adjust();
|
||||
boost::posix_time::time_duration td (ts.hour, ts.min, ts.sec, ts.msec * (ticks_per_second / 1000));
|
||||
return PTime(dt, td);
|
||||
}
|
||||
|
||||
#ifndef NO_CPP0X
|
||||
extern "C" BTS_EXPORT LLDateTime ll_today() {
|
||||
return datetime::ll_from_date(today());
|
||||
}
|
||||
|
||||
BTS_EXPORT Date today() {
|
||||
auto now = datetime::now();
|
||||
Date date;
|
||||
date.year = now.date().year();
|
||||
date.month = now.date().month();
|
||||
date.day = now.date().day();
|
||||
return date;
|
||||
}
|
||||
BTS_EXPORT Date add_days(Date indate, int days) {
|
||||
boost::gregorian::date dt(indate.year, indate.month, indate.day);
|
||||
auto adjusted_dt = dt + boost::gregorian::days(days);
|
||||
Date date(adjusted_dt.year(), adjusted_dt.month(), adjusted_dt.day());
|
||||
return date;
|
||||
}
|
||||
|
||||
extern "C" BTS_EXPORT LLDateTime ll_yesterday() {
|
||||
auto now = datetime::now() - boost::gregorian::days(1);
|
||||
return datetime::ll_from_parts(now.date().year(), now.date().month(), now.date().day(), 0, 0, 0, 0);
|
||||
}
|
||||
#endif
|
||||
}}
|
||||
@@ -151,10 +151,12 @@ std::string get_server_info(const api::ServiceContext& ctx) {
|
||||
|
||||
if (response.success()) {
|
||||
ss << " \"users\": [" << endl;
|
||||
bool first = true;
|
||||
for (const auto& user : response.result()->users()) {
|
||||
if (user.is_present()) {
|
||||
ss << " ";
|
||||
if (&user != &response.result()->users()[0]) ss << ",";
|
||||
if (first) { first = false; }
|
||||
else { ss << ","; }
|
||||
ss << "{ ";
|
||||
ss << "\"name\": \"" << user.name() << "\", ";
|
||||
ss << "\"operator_id\": \"" << user.user_data().exchange_user_data().cme_data().operator_id() << "\"";
|
||||
|
||||
@@ -653,7 +653,7 @@ let exchangeSettings model dispatch =
|
||||
Fields.enumSelect "CTI" (toHuman false CTICode.values cme.cti) (allToHuman false CTICode.values) [] (fromHuman CTICode.values>>CMEExchangeField.CTI>>send)
|
||||
Fields.enumSelect "Origin" (toHuman false CustomerOrFirm.values cme.origin) (allToHuman false CustomerOrFirm.values) [] (fromHuman CustomerOrFirm.values>>CMEExchangeField.Origin>>send)
|
||||
//Fields.text "Takeup Member Code" cme.takeupMemberCode [] (TakeupMember>>send)
|
||||
Fields.text "Clearing Account" cme.clearingAccount [] (CMEExchangeField.ClearingAccount>>send)
|
||||
Fields.text "Clearing Account" cme.clearingAccount [MaxLength 12.0] (CMEExchangeField.ClearingAccount>>send)
|
||||
//Fields.text "Direct Electronic Access" cme.directElectronicAccess [] (DirectElectronicAccess>>send)
|
||||
//Fields.text "Trading Capacity" cme.tradingCapacity [] (TradingCapacity>>send)
|
||||
//Fields.text "Liquidity Provision" cme.liquidityProvision [] (LiquidityProvision>>send)
|
||||
@@ -661,7 +661,7 @@ let exchangeSettings model dispatch =
|
||||
h2 [Class "is-size-3"] [ str "SIM" ]
|
||||
Fields.enumSelect "CTI" (toHuman false CTICode.values sim.cti) (allToHuman false CTICode.values) [] (fromHuman CTICode.values>>SIMExchangeField.CTI>>sendSim)
|
||||
Fields.enumSelect "Origin" (toHuman false CustomerOrFirm.values sim.origin) (allToHuman false CustomerOrFirm.values) [] (fromHuman CustomerOrFirm.values>>SIMExchangeField.Origin>>sendSim)
|
||||
Fields.text "Clearing Account" sim.clearingAccount [] (SIMExchangeField.ClearingAccount>>sendSim)
|
||||
Fields.text "Clearing Account" sim.clearingAccount [MaxLength 12.0] (SIMExchangeField.ClearingAccount>>sendSim)
|
||||
|
||||
]
|
||||
|
||||
|
||||
@@ -226,8 +226,8 @@ let update msg (model:Users.Types.Model) : Model * Cmd<Msg> =
|
||||
model, Cmd.Empty
|
||||
| UpdateField field ->
|
||||
{ model with scratchUser = applyUpdate field model.scratchUser; ConfirmModal = ("", Msg.SwitchTab model.currentTab) } , Cmd.Empty
|
||||
| Clone usr ->
|
||||
{ model with scratchUser = { usr with id = 0u; name = usr.name + "_copy" }}, Cmd.Empty
|
||||
| CloneUser ->
|
||||
{ model with PreviousScratchUser = model.scratchUser; scratchUser = { model.scratchUser with id = 0u; name = sprintf "%s_clone" model.scratchUser.name }; OptionsMenuOpen = ""}, Cmd.ofMsg (SwitchTab General)
|
||||
| CopyFrom (usrSrc, usrDest) ->
|
||||
let nusr = { usrSrc with
|
||||
id = usrDest.id
|
||||
|
||||
@@ -36,7 +36,7 @@ type Msg =
|
||||
| RpcResponse of string
|
||||
| UpdateAdminData of AdminData
|
||||
| UpdateField of UserField
|
||||
| Clone of BTSUser
|
||||
| CloneUser
|
||||
| CopyFrom of BTSUser * BTSUser
|
||||
| Delete of BTSUser
|
||||
| AddUser
|
||||
|
||||
@@ -152,7 +152,7 @@ let orderRoutingTabView (model:Model) (dispatch:Msg->Unit) =
|
||||
h2 [Class "is-size-3"] [ str "CME" ]
|
||||
div []
|
||||
[
|
||||
Fields.text "Operator ID" cmeData.operatorId [] (CMEOperatorID >> UpdateField >> dispatch)
|
||||
Fields.text "Operator ID" cmeData.operatorId [ MaxLength 18.0 ] (CMEOperatorID >> UpdateField >> dispatch)
|
||||
//Fields.text "ATS Operator ID" cmeData.atsOperatorId [] (ATSOperatorID >> UpdateField >> dispatch)
|
||||
Fields.text "SMP ID" cmeData.smpId [] (SMPID >> UpdateField >> dispatch)
|
||||
Fields.mapSelect "SMP Instruction" State.SMPInstructionMap None (Some cmeData.smpInstruction) (SMPInstruction >> UpdateField >> dispatch)
|
||||
@@ -164,7 +164,7 @@ let orderRoutingTabView (model:Model) (dispatch:Msg->Unit) =
|
||||
h2 [Class "is-size-3"] [ str "Sim" ]
|
||||
div []
|
||||
[
|
||||
Fields.text "Operator ID" simData.operatorId [] (SimOperatorID >> UpdateField >> dispatch)
|
||||
Fields.text "Operator ID" simData.operatorId [ MaxLength 18.0 ] (SimOperatorID >> UpdateField >> dispatch)
|
||||
Fields.text "Location" simData.location [] (SimLocation >> UpdateField >> dispatch)
|
||||
|
||||
]
|
||||
@@ -178,27 +178,6 @@ let userControls (model:Model) (dispatch:Msg->Unit) =
|
||||
[
|
||||
saveButton model dispatch
|
||||
Button.button "Revert" "" "" (if dirty then Button.Enabled else Button.Disabled) (fun _ -> SwitchUser (currentUser) |> dispatch)
|
||||
(*p [ ClassName "control" ]
|
||||
[ a [ ClassName "button is-light"
|
||||
Title "Clone"
|
||||
OnClick (fun _ -> dispatch <| Clone currentUser)
|
||||
Disabled disabled
|
||||
]
|
||||
[ span [ ClassName "icon is-small" ]
|
||||
[ i [ ClassName "fa fa-clone" ]
|
||||
[ ] ]
|
||||
]
|
||||
]*)
|
||||
(*p [ ClassName "control" ]
|
||||
[ a [ ClassName "button is-light" ]
|
||||
[ span [ ClassName "icon is-small"
|
||||
Title "Copy from user 1 ..."
|
||||
OnClick (fun _ -> dispatch <| CopyFrom (usersList.Head, currentUser))
|
||||
]
|
||||
[ i [ ClassName "fa fa-copy" ]
|
||||
[ ] ]
|
||||
]
|
||||
]*)
|
||||
]
|
||||
|
||||
|
||||
@@ -276,7 +255,11 @@ let userSelectionGrid (usersList:BTSUser list) (currentUser:BTSUser) (dispatch:M
|
||||
|
||||
let menuItem (user:BTSUser) (selectedUser:BTSUser) model dispatch =
|
||||
a [ ClassName (isActiveUser user.id selectedUser)
|
||||
OnClick (fun _ -> SwitchUser user |> dispatch)
|
||||
OnClick (fun _ ->
|
||||
match user.name = model.OptionsMenuOpen with
|
||||
| false -> SwitchUser user |> dispatch
|
||||
| _ -> ()
|
||||
)
|
||||
] [
|
||||
span [] [ str user.name ]
|
||||
span [ Class "is-pulled-right menu-icon" ]
|
||||
@@ -305,6 +288,10 @@ let menuItem (user:BTSUser) (selectedUser:BTSUser) model dispatch =
|
||||
OnClick (fun _ -> dispatch <| Delete user)
|
||||
]
|
||||
[ str "Remove" ]
|
||||
a [ Class "dropdown-item"
|
||||
OnClick (fun _ -> dispatch <| CloneUser)
|
||||
]
|
||||
[ str "Clone" ]
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import sys, os
|
||||
import socket, binascii
|
||||
|
||||
if len(sys.argv) < 3:
|
||||
print "Usage: udp.py <group> <port> <listen address>"
|
||||
exit(0)
|
||||
mcast_grp = sys.argv[1]
|
||||
mcast_port = int(sys.argv[2])
|
||||
listen_addr = len(sys.argv) == 4 and sys.argv[3] or '0.0.0.0'
|
||||
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
|
||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 32)
|
||||
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_LOOP, 1)
|
||||
sock.bind((mcast_grp, mcast_port))
|
||||
|
||||
host = listen_addr
|
||||
sock.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_IF, socket.inet_aton(host))
|
||||
sock.setsockopt(socket.SOL_IP, socket.IP_ADD_MEMBERSHIP, socket.inet_aton(mcast_grp) + socket.inet_aton(host))
|
||||
|
||||
while 1:
|
||||
try:
|
||||
data, addr = sock.recvfrom(1024)
|
||||
except socket.error, e:
|
||||
print "error", e
|
||||
hexdata = binascii.hexlify(data)
|
||||
print 'Data = %s' % hexdata
|
||||
Reference in new issue
Block a user