Compare commits

...
10 changed files with 69 additions and 138 deletions

No files matched your search

@@ -80,7 +80,7 @@ namespace BCG {
int id = Convert.ToInt32(skey);
PriceStats pstats = kv.Value;
var today = DateTime.Now.Date;
IBlueRow r = new BlueRow(_pstats_scheam, id, skey, pstats.security.ToString(), today, pstats.open, pstats.close, pstats.settlement);
IBlueRow r = new BlueRow(_pstats_scheam, id, skey, pstats.Security.ToString(), today, pstats.Open, pstats.Close, pstats.Settlement);
new_state.addRow(r);
}
DataPacket dp;
@@ -29,7 +29,7 @@ using System.Reactive.Subjects;
using System.Threading;
using System.Threading.Tasks;
public class MarketDataClient : IMarketDataClient {
public class MarketDataClient : IMarketDataClient, IDisposable {
readonly ISubject<MDConsumer> mdConsumerSrc = new ReplaySubject<MDConsumer>(1);
readonly IObservable<ConcurrentDictionary<uint, SecurityMD>> securitiesSrc;
readonly List<IDisposable> _resources = new();
@@ -195,7 +195,7 @@ public class MarketDataClient : IMarketDataClient {
PollStats);
uint GetSecId(PriceStats stats) =>
stats.security.Id;
stats.Security.Id;
TableUpdate<PriceStats, uint> PollStats(TableUpdate<PriceStats, uint> table, long _) {
var bd = table.ToBuilder();
@@ -229,7 +229,7 @@ public class MarketDataClient : IMarketDataClient {
PollTickers);
uint GetSecId(PriceTicker ticker) =>
ticker.security.Id;
ticker.Security.Id;
TableUpdate<PriceTicker, uint> PollTickers(TableUpdate<PriceTicker, uint> table, long _) {
var bd = table.ToBuilder();
@@ -243,6 +243,9 @@ public class MarketDataClient : IMarketDataClient {
}
}
~MarketDataClient() =>
Dispose();
public void Dispose() {
GC.SuppressFinalize(this);
isDisposed = true;
@@ -16,8 +16,10 @@ class PollablePriceTicker {
PriceTicker ticker;
ulong volume;
public PollablePriceTicker(SecurityMD securityMd) =>
public PollablePriceTicker(SecurityMD securityMd) {
this.securityMd = securityMd;
ticker = mkTicker();
}
public PriceTicker ToTicker() =>
ticker;
+10 -36
View File
@@ -2,46 +2,20 @@
namespace Spark.Api.MarketData {
using bts.api;
using System;
/// <summary>
/// Price/trade statistics for a particular security (slow moving)
/// </summary>
public readonly struct PriceStats : IEquatable<PriceStats>, IJsonFormatter<PriceStats> {
public PriceStats(SecurityImmutable security, double open, double close, double settlement) {
this.security = security;
this.open = open;
this.close = close;
this.settlement = settlement;
}
public SecurityImmutable security { get; }
public double open { get; }
public double close { get; }
public double settlement { get; }
public bool Equals(PriceStats other) =>
security.Equals(other.security) &&
open.Equals(other.open) &&
close.Equals(other.close) &&
settlement.Equals(other.settlement);
public override bool Equals(object? obj) =>
obj is PriceStats other && Equals(other);
public override int GetHashCode() =>
HashCode.Combine(security.Id, open, close, settlement);
public static bool operator ==(PriceStats left, PriceStats right) =>
left.Equals(right);
public static bool operator !=(PriceStats left, PriceStats right) =>
!(left == right);
public readonly record struct PriceStats(
SecurityImmutable Security,
double Open,
double Close,
double Settlement
) : IJsonFormatter<PriceStats> {
public static System.Object ToJsonObj(PriceStats obj) => new {
open = obj.open,
close = obj.close,
settlement = obj.settlement,
security = obj.security.Exchange.ToString() + "." + obj.security.ToString()
open = obj.Open,
close = obj.Close,
settlement = obj.Settlement,
security = obj.Security.Exchange.ToString() + "." + obj.Security.ToString()
};
}
}
+25 -96
View File
@@ -3,106 +3,35 @@ namespace Spark.Api.MarketData {
using bts.api;
using bts.api.domain;
using Spark.Core.Collections;
using System;
/// <summary>
/// Price/trade statistics for a particular security (fast moving)
/// </summary>
public readonly struct PriceTicker : IEquatable<PriceTicker>, IJsonFormatter<PriceTicker> {
public PriceTicker(
SecurityImmutable security,
TickDisplaySettingsImmutable quantity_display,
double high,
double low,
double volume,
double bid,
double bid_quantity,
double ask,
double ask_quantity,
double mark,
double last,
double last_quantity
) {
this.security = security;
this.quantity_display = quantity_display;
this.high = high;
this.low = low;
this.volume = volume;
this.bid = bid;
this.bid_quantity = bid_quantity;
this.ask = ask;
this.ask_quantity = ask_quantity;
this.mark = mark;
this.last = last;
this.last_quantity = last_quantity;
}
public SecurityImmutable security { get; }
public TickDisplaySettingsImmutable quantity_display { get; }
public double high { get; }
public double low { get; }
public double volume { get; }
public double bid { get; }
public double bid_quantity { get; }
public double ask { get; }
public double ask_quantity { get; }
public double mark { get; }
public double last { get; }
public double last_quantity { get; }
public bool Equals(PriceTicker other) =>
security.Equals(other.security) &&
quantity_display.Equals(other.quantity_display) &&
high.Equals(other.high) &&
low.Equals(other.low) &&
volume.Equals(other.volume) &&
bid.Equals(other.bid) &&
bid_quantity.Equals(other.bid_quantity) &&
ask.Equals(other.ask) &&
ask_quantity.Equals(other.ask_quantity) &&
mark.Equals(other.mark) &&
last.Equals(other.last) &&
last_quantity.Equals(other.last_quantity);
public override bool Equals(object? obj) =>
obj is PriceTicker other && Equals(other);
public override int GetHashCode() =>
new HashCode()
.with_values(new int[] {
security.Id.GetHashCode(),
high.GetHashCode(),
low.GetHashCode(),
volume.GetHashCode(),
bid.GetHashCode(),
bid_quantity.GetHashCode(),
ask.GetHashCode(),
ask_quantity.GetHashCode(),
mark.GetHashCode(),
last.GetHashCode(),
last_quantity.GetHashCode()
})
.ToHashCode();
public static bool operator ==(PriceTicker left, PriceTicker right) =>
left.Equals(right);
public static bool operator !=(PriceTicker left, PriceTicker right) =>
!(left == right);
public readonly record struct PriceTicker (
SecurityImmutable Security,
TickDisplaySettingsImmutable QuantityDisplay,
double High,
double Low,
double Volume,
double Bid,
double BidQuantity,
double Ask,
double AskQuantity,
double Mark,
double Last,
double LastQuantity
) : IJsonFormatter<PriceTicker> {
public static System.Object ToJsonObj(PriceTicker obj) => new {
security = obj.security.Exchange.ToString() + "." + obj.security.ToString(),
high = obj.high,
low = obj.low,
volume = obj.volume,
bid = obj.bid,
bid_quantity = obj.bid_quantity,
ask = obj.ask,
ask_quantity = obj.ask_quantity,
mark = obj.mark,
last = obj.last,
last_quantity = obj.last_quantity
security = obj.Security.Exchange.ToString() + "." + obj.Security.ToString(),
high = obj.High,
low = obj.Low,
volume = obj.Volume,
bid = obj.Bid,
bid_quantity = obj.BidQuantity,
ask = obj.Ask,
ask_quantity = obj.AskQuantity,
mark = obj.Mark,
last = obj.Last,
last_quantity = obj.LastQuantity
};
}
}
+2 -1
View File
@@ -139,7 +139,7 @@ class TopOfBookQuoter : IHostedService {
// Send a new order for this security.
async void SendOrder() {
logger.LogInformation("Sending order. {side} {security} {quantity} @ {price}", orderId, bk.Security, orderQty, orderPrice);
var res = await sparkClient.CreateLimitOrder(config.Account!, bk.SecurityId, Side.Buy, orderQty, orderPrice);
var res = await sparkClient.CreateLimitOrder(config.Account!, bk.SecurityId, Side.Buy, orderQty, orderPrice, null);
if (res.ErrorCode == bts.api.orderman.OrderError.Ok) {
logger.LogDebug("Sent order. id: {orderId}, security: {security}", orderId, bk.Security);
} else {
@@ -151,6 +151,7 @@ class TopOfBookQuoter : IHostedService {
public Task StopAsync(CancellationToken cancellationToken) {
logger.LogInformation("RequoteTopOfBookDemo stopping");
Disposable.Dispose(ref subscription);
logger.LogInformation("RequoteTopOfBookDemo stopped");
return Task.CompletedTask;
}
@@ -91,6 +91,12 @@ public class AuthenticatedSimpleSparkClient : ISimpleSparkClient, IHostedService
public IObservable<IList<TradeInfo>> Trades(SecurityCriteria? securityCriteria) =>
simpleClient?.Trades(securityCriteria) ?? simpleClientSubject.Take(1).Select(c => c.Trades(securityCriteria)).FlattenSeries();
public IObservable<TableUpdate<PriceStats, uint>> PriceStats(SecurityCriteria? securityCriteria) =>
simpleClient?.PriceStats(securityCriteria) ?? simpleClientSubject.Take(1).Select(c => c.PriceStats(securityCriteria)).FlattenSeries();
public IObservable<TableUpdate<PriceTicker, uint>> PriceTicker(SecurityCriteria? securityCriteria) =>
simpleClient?.PriceTicker(securityCriteria) ?? simpleClientSubject.Take(1).Select(c => c.PriceTicker(securityCriteria)).FlattenSeries();
public Task<Response> CreateOrder(OrderDefinition orderDef, double quantity, double price) =>
orderOp(cli => cli.CreateOrder(orderDef, quantity, price));
@@ -4,6 +4,7 @@ using bts.api;
using bts.api.core;
using bts.api.orderman;
using global::Spark.Api.MarketData;
using global::Spark.Api.Orders;
using mbts.spark;
@@ -41,6 +42,12 @@ public class ExecutionFacadeSimpleSparkClient : ISimpleSparkClient {
public IObservable<IList<TradeInfo>> Trades(SecurityCriteria? securityCriteria) =>
sparkClient.Trades(securityCriteria);
public IObservable<TableUpdate<PriceStats, uint>> PriceStats(SecurityCriteria? securityCriteria) =>
sparkClient.PriceStats(securityCriteria);
public IObservable<TableUpdate<PriceTicker, uint>> PriceTicker(SecurityCriteria? securityCriteria) =>
sparkClient.PriceTicker(securityCriteria);
public Task<Response> CreateOrder(OrderDefinition orderDef, double quantity, double price) {
logger.LogInformation("CreateOrder {account} {tag} {securityId} {side} {quantity} @ {price}", orderDef.Account, orderDef.Tag, orderDef.SecurityKey, orderDef.Side, quantity, price);
return Task.FromResult(facadeResponse());
@@ -4,6 +4,7 @@ using bts.api;
using bts.api.core;
using bts.api.orderman;
using global::Spark.Api.MarketData;
using global::Spark.Api.Orders;
using mbts.spark;
@@ -15,6 +16,8 @@ public interface ISimpleSparkClient {
IObservable<TableUpdate<SecurityImmutable, uint>> Securities(SecurityCriteria? securityCriteria);
IObservable<TableUpdate<BookRecord, uint>> Books(SecurityCriteria? securityCriteria);
IObservable<IList<TradeInfo>> Trades(SecurityCriteria? securityCriteria);
IObservable<TableUpdate<PriceStats, uint>> PriceStats(SecurityCriteria? securityCriteria);
IObservable<TableUpdate<PriceTicker, uint>> PriceTicker(SecurityCriteria? securityCriteria);
// Orders, Positions, and Pnl
IObservable<TableUpdate<OrderRecordTupleWithRoot, uint>> Orders(string? account, SecurityCriteria? securityCriteria);
@@ -249,6 +249,12 @@ public class SimpleSparkClient : ISimpleSparkClient, IDisposable {
: Observable.Return<PositionRecord?>(null))
.FlattenSeries();
public IObservable<TableUpdate<PriceStats, uint>> PriceStats(SecurityCriteria? securityCriteria) =>
mdClient.PriceStats(securityCriteria);
public IObservable<TableUpdate<PriceTicker, uint>> PriceTicker(SecurityCriteria? securityCriteria) =>
mdClient.PriceTicker(securityCriteria);
public IObservable<IList<TradeInfo>> Trades(SecurityCriteria? securityCriteria) {
var tradesSource = mdClient.Trades();
if (securityCriteria?.is_defined() ?? false) {