diff --git a/Client/Client.csproj b/Client/Client.csproj index 885878c23..c065fd5ae 100644 --- a/Client/Client.csproj +++ b/Client/Client.csproj @@ -7,12 +7,13 @@ - - - + + + + diff --git a/Client/Components/BaseComponent.razor.cs b/Client/Components/BaseComponent.razor.cs index 9e2437d26..80361fcd9 100644 --- a/Client/Components/BaseComponent.razor.cs +++ b/Client/Components/BaseComponent.razor.cs @@ -1,6 +1,7 @@ +using Distribution.Services; using Microsoft.AspNetCore.Components; -using Schedule.Runners; using System; +using System.Threading; using System.Threading.Tasks; namespace Client.Components @@ -10,23 +11,16 @@ public class BaseComponent : ComponentBase /// /// Updater /// - protected virtual TimeRunner Updater { get; set; } = new() - { - Count = 1, - Span = TimeSpan.FromMilliseconds(100) - }; + protected virtual ScheduleService Updater { get; set; } = new ScheduleService(); /// /// Render /// - protected virtual Task Render(Action action) + protected virtual Task Render(Action action) => Updater.Send(() => { - return Updater.Send(() => - { - action(); - InvokeAsync(StateHasChanged); - - }).Task; - } + action(); + InvokeAsync(StateHasChanged); + Thread.Sleep(1); + }).Task; } } diff --git a/Client/Components/ChartsComponent.razor.cs b/Client/Components/ChartsComponent.razor.cs index a7aea6d0f..6c5220279 100644 --- a/Client/Components/ChartsComponent.razor.cs +++ b/Client/Components/ChartsComponent.razor.cs @@ -11,7 +11,7 @@ namespace Client.Components { - public partial class ChartsComponent : IDisposable, IAsyncDisposable + public partial class ChartsComponent : IDisposable { /// /// Reference to view control @@ -89,7 +89,7 @@ public virtual Task UpdateItems(IList> inputs, var domain = new DomainModel { - IndexDomain = new[] { Shapes.Count - (count ?? Shapes.Count), Shapes.Count } + IndexDomain = new int[] { Shapes.Count - (count ?? Shapes.Count), Shapes.Count } }; return Render(() => View.Update(domain, Shapes)); @@ -108,20 +108,6 @@ public virtual void Clear() /// /// Dispose /// - /// - public virtual ValueTask DisposeAsync() - { - Dispose(); - - return new ValueTask(Task.CompletedTask); - } - - /// - /// Dispose - /// - public virtual void Dispose() - { - View?.DisposeAsync(); - } + public virtual void Dispose() => View?.Dispose(); } } diff --git a/Client/Components/PageComponent.razor.cs b/Client/Components/PageComponent.razor.cs index 33adb1a97..1f9ae9250 100644 --- a/Client/Components/PageComponent.razor.cs +++ b/Client/Components/PageComponent.razor.cs @@ -1,10 +1,9 @@ using Microsoft.AspNetCore.Components; using Microsoft.Extensions.Configuration; -using Schedule.Runners; using System; +using System.Linq; using System.Threading.Tasks; using Terminal.Core.Domains; -using Terminal.Core.Services; namespace Client.Components { @@ -23,43 +22,69 @@ public partial class PageComponent public virtual OrdersComponent OrdersView { get; set; } public virtual PositionsComponent PositionsView { get; set; } public virtual StatementsComponent StatementsView { get; set; } - public virtual IConnector Adapter { get; set; } + public virtual IGateway Adapter { get; set; } public virtual Action Setup { get; set; } public virtual async Task OnConnect() { - OnDisconnect(); - Setup(); + try + { + OnDisconnect(); + Setup(); - IsConnection = true; - IsSubscription = true; + IsConnection = true; + IsSubscription = true; - await Adapter.Connect(); + await Adapter.Connect(); + } + catch (Exception e) + { + Console.WriteLine(e); + } } public virtual void OnDisconnect() { - IsConnection = false; - IsSubscription = false; + try + { + Adapter?.Disconnect(); - Adapter?.Disconnect(); + ChartsView.Clear(); + ReportsView.Clear(); - ChartsView.Clear(); - ReportsView.Clear(); + IsConnection = false; + IsSubscription = false; + } + catch (Exception e) + { + Console.WriteLine(e); + } } - public virtual async Task OnSubscribe() + public virtual void OnSubscribe() { - IsSubscription = true; - - await Adapter.Subscribe(); + try + { + IsSubscription = true; + Adapter.Account.Instruments.ForEach(o => Adapter.Subscribe(o.Key)); + } + catch (Exception e) + { + Console.WriteLine(e); + } } public virtual void OnUnsubscribe() { - IsSubscription = false; - - Adapter.Unsubscribe(); + try + { + IsSubscription = false; + Adapter.Account.Instruments.ForEach(o => Adapter.Unsubscribe(o.Key)); + } + catch (Exception e) + { + Console.WriteLine(e); + } } public virtual void OnOpenStatements() diff --git a/Client/Pages/Pairs.razor.cs b/Client/Pages/Pairs.razor.cs index c3fbdf42c..02552a8db 100644 --- a/Client/Pages/Pairs.razor.cs +++ b/Client/Pages/Pairs.razor.cs @@ -1,25 +1,22 @@ using Canvas.Core.Models; using Canvas.Core.Shapes; +using Client.Components; using Microsoft.AspNetCore.Components; using Microsoft.Extensions.Configuration; -using Schedule.Runners; +using Simulation; using SkiaSharp; +using System; using System.Collections.Generic; using System.Linq; -using System.Reactive.Linq; using System.Threading.Tasks; -using Client.Components; -using Terminal.Connector.Simulation; using Terminal.Core.Domains; using Terminal.Core.Enums; using Terminal.Core.Indicators; using Terminal.Core.Models; -using Terminal.Core.Services; -using System; namespace Client.Pages { - public partial class Pairs + public partial class Pairs : IDisposable { [Inject] IConfiguration Configuration { get; set; } @@ -28,7 +25,6 @@ public partial class Pairs /// const string _assetX = "GOOGL"; const string _assetY = "GOOG"; - const string _account = "Simulation"; protected virtual IAccount Account { get; set; } protected virtual PageComponent View { get; set; } @@ -38,49 +34,35 @@ protected override async Task OnAfterRenderAsync(bool setup) { if (setup) { - var comUp = new ComponentModel { Color = SKColors.DeepSkyBlue }; - var comDown = new ComponentModel { Color = SKColors.OrangeRed }; + var indUp = new ComponentModel { Color = SKColors.DeepSkyBlue }; + var indDown = new ComponentModel { Color = SKColors.OrangeRed }; + var indAreas = new GroupShape(); + var indCharts = new GroupShape(); - await View.ChartsView.Create(new GroupShape - { - Groups = new Dictionary - { - ["Prices"] = new GroupShape - { - Groups = new Dictionary - { - [nameof(OrderSideEnum.Buy)] = new ArrowShape { Component = comUp }, - [nameof(OrderSideEnum.Sell)] = new ArrowShape { Component = comDown }, - ["Range"] = new AreaShape { Component = comUp } - } - } - } - }); + indCharts.Groups["Buy"] = new ArrowShape { Component = indUp }; + indCharts.Groups["Sell"] = new ArrowShape { Component = indDown }; + indCharts.Groups["Range"] = new AreaShape { Component = indUp }; + indAreas.Groups["Prices"] = indCharts; - var componentGain = new ComponentModel { Color = SKColors.OrangeRed, Size = 5 }; - var componentBalance = new ComponentModel { Color = SKColors.Black }; + await View.ChartsView.Create(indAreas); - await View.ReportsView.Create(new GroupShape - { - Groups = new Dictionary - { - ["Performance"] = new GroupShape - { - Groups = new Dictionary - { - ["PnL"] = new LineShape { Component = componentGain }, - ["Balance"] = new AreaShape { Component = componentBalance } - } - } - } - }); + var pnlGain = new ComponentModel { Color = SKColors.OrangeRed, Size = 5 }; + var pnlBalance = new ComponentModel { Color = SKColors.Black }; + var pnlAreas = new GroupShape(); + var pnlCharts = new GroupShape(); + + pnlCharts.Groups["PnL"] = new LineShape { Component = pnlGain }; + pnlCharts.Groups["Balance"] = new AreaShape { Component = pnlBalance }; + pnlAreas.Groups["Performance"] = pnlCharts; + + await View.ReportsView.Create(pnlAreas); View.Setup = () => { Account = new Account { + Name = "Demo", Balance = 25000, - Name = _account, Instruments = new Dictionary { [_assetX] = new Instrument { Name = _assetX }, @@ -100,13 +82,10 @@ await View.ReportsView.Create(new GroupShape Account .Instruments .Values - .ForEach(o => o.Points.CollectionChanged += (o, e) => - { - foreach (PointModel item in e.NewItems) - { - InstanceService.Instance.Send(OnData(item)); - } - }); + .ForEach(o => o.Points.CollectionChanged += (_, e) => e + .NewItems + .OfType() + .ForEach(async o => await OnData(o))); }; } @@ -175,38 +154,30 @@ private async Task OnData(PointModel point) private (string, string) OpenPositions(IInstrument assetBuy, IInstrument assetSell) { - var messageSell = new StateModel + var orderSell = new OrderModel { - Action = ActionEnum.Create, - Next = new OrderModel + Side = OrderSideEnum.Sell, + Type = OrderTypeEnum.Market, + Transaction = new() { - Side = OrderSideEnum.Sell, - Type = OrderTypeEnum.Market, - Transaction = new() - { - Volume = 1, - Instrument = assetSell - } + Volume = 1, + Instrument = assetSell } }; - var messageBuy = new StateModel + var orderBuy = new OrderModel { - Action = ActionEnum.Create, - Next = new OrderModel + Side = OrderSideEnum.Buy, + Type = OrderTypeEnum.Market, + Transaction = new() { - Side = OrderSideEnum.Buy, - Type = OrderTypeEnum.Market, - Transaction = new() - { - Volume = 1, - Instrument = assetBuy - } + Volume = 1, + Instrument = assetBuy } }; - View.Adapter.OrderStream(messageSell); - View.Adapter.OrderStream(messageBuy); + View.Adapter.CreateOrders(orderBuy); + View.Adapter.CreateOrders(orderSell); var account = View.Adapter.Account; var buy = account.ActivePositions.Values.First(o => o.Order.Side == OrderSideEnum.Buy); @@ -215,7 +186,7 @@ private async Task OnData(PointModel point) //points.Add(new PointModel { Time = buy.Time, Name = nameof(OrderSideEnum.Buy), Last = buy.OpenPrices.Last().Price }); //points.Add(new PointModel { Time = sell.Time, Name = nameof(OrderSideEnum.Sell), Last = sell.OpenPrices.Last().Price }); - return (messageSell.Next.Transaction.Id, messageBuy.Next.Transaction.Id); + return (orderSell.Transaction.Id, orderBuy.Transaction.Id); } private void ClosePositions() @@ -223,12 +194,9 @@ private void ClosePositions() foreach (var position in View.Adapter.Account.ActivePositions.Values) { var side = OrderSideEnum.Buy; - var point = position.Order.Transaction.Instrument.Points.Last(); - var price = point.Ask; if (Equals(position.Order.Side, OrderSideEnum.Buy)) { - price = point.Bid; side = OrderSideEnum.Sell; } @@ -243,14 +211,12 @@ private void ClosePositions() } }; - View.Adapter.OrderStream(new StateModel - { - Action = ActionEnum.Create, - Next = order - }); + View.Adapter.CreateOrders(order); //points.Add(new PointModel { Time = order.Time, Name = nameof(OrderSideEnum.Buy), Last = price }); } } + + public void Dispose() => View?.Adapter?.Disconnect(); } } diff --git a/Core/Core.csproj b/Core/Core.csproj index 656ff1ade..87a4023ce 100644 --- a/Core/Core.csproj +++ b/Core/Core.csproj @@ -14,10 +14,9 @@ - + + - - diff --git a/Core/Domains/Connector.cs b/Core/Domains/Connector.cs deleted file mode 100644 index 212d36546..000000000 --- a/Core/Domains/Connector.cs +++ /dev/null @@ -1,293 +0,0 @@ -using FluentValidation.Results; -using Mapper; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Terminal.Core.Enums; -using Terminal.Core.Models; -using Terminal.Core.Services; -using Terminal.Core.Validators; - -namespace Terminal.Core.Domains -{ - public interface IConnector : IDisposable - { - /// - /// Production or Development mode - /// - EnvironmentEnum Mode { get; set; } - - /// - /// Account - /// - IAccount Account { get; set; } - - /// - /// Incoming data event - /// - Action> DataStream { get; set; } - - /// - /// Send order event - /// - Action> OrderStream { get; set; } - - /// - /// Restore state and initialize - /// - Task> Connect(); - - /// - /// Save state and dispose - /// - Task> Disconnect(); - - /// - /// Continue execution - /// - Task> Subscribe(); - - /// - /// Suspend execution - /// - Task> Unsubscribe(); - - /// - /// Get quote - /// - /// - Task> GetPoint(PointMessageModel message); - - /// - /// Get quotes history - /// - /// - Task>> GetPoints(PointMessageModel message); - - /// - /// Get option chains - /// - /// - Task>> GetOptions(OptionMessageModel message); - - /// - /// Send new orders - /// - /// - Task> CreateOrders(params OrderModel[] orders); - - /// - /// Update orders - /// - /// - Task> UpdateOrders(params OrderModel[] orders); - - /// - /// Cancel orders - /// - /// - Task> DeleteOrders(params OrderModel[] orders); - } - - /// - /// Implementation - /// - public abstract class Connector : IConnector - { - /// - /// Production or Sandbox - /// - public virtual EnvironmentEnum Mode { get; set; } - - /// - /// Account - /// - public virtual IAccount Account { get; set; } - - /// - /// Incoming data event - /// - public virtual Action> DataStream { get; set; } - - /// - /// Send order event - /// - public virtual Action> OrderStream { get; set; } - - /// - /// Constructor - /// - public Connector() - { - Mode = EnvironmentEnum.Paper; - - DataStream = o => { }; - OrderStream = o => { }; - } - - /// - /// Restore state and initialize - /// - public abstract Task> Connect(); - - /// - /// Continue execution - /// - public abstract Task> Subscribe(); - - /// - /// Save state and dispose - /// - public abstract Task> Disconnect(); - - /// - /// Unsubscribe from data streams - /// - public abstract Task> Unsubscribe(); - - /// - /// Get quote - /// - /// - public abstract Task> GetPoint(PointMessageModel message); - - /// - /// Get quotes history - /// - /// - public abstract Task>> GetPoints(PointMessageModel message); - - /// - /// Get option chains - /// - /// - public abstract Task>> GetOptions(OptionMessageModel message); - - /// - /// Send new orders - /// - /// - public abstract Task> CreateOrders(params OrderModel[] orders); - - /// - /// Update orders - /// - /// - public abstract Task> UpdateOrders(params OrderModel[] orders); - - /// - /// Cancel orders - /// - /// - public abstract Task> DeleteOrders(params OrderModel[] orders); - - /// - /// Dispose - /// - public virtual void Dispose() => Disconnect(); - - /// - /// Set missing order properties - /// - /// - protected virtual IList CorrectOrders(params OrderModel[] orders) - { - foreach (var nextOrder in orders) - { - nextOrder.Type ??= OrderTypeEnum.Market; - nextOrder.TimeSpan ??= OrderTimeSpanEnum.GTC; - nextOrder.Transaction ??= new TransactionModel(); - nextOrder.Transaction.Time ??= DateTime.Now; - nextOrder.Transaction.Price ??= GetOpenPrice(nextOrder); - nextOrder.Transaction.Status ??= OrderStatusEnum.None; - nextOrder.Transaction.Operation ??= OperationEnum.In; - } - - return orders; - } - - /// - /// Ensure all properties have correct values - /// - /// - protected virtual ResponseModel ValidateOrders(params OrderModel[] orders) - { - var map = Mapper.Map; - var orderRules = InstanceService.Instance; - var response = new ResponseModel(); - - foreach (var order in orders) - { - var errors = new List(); - - errors.AddRange(orderRules.Validate(order).Errors.Select(o => map(o, new ErrorModel()))); - errors.AddRange(order.Orders.SelectMany(o => orderRules.Validate(o).Errors.Select(o => map(o, new ErrorModel())))); - - response.Count += errors.Count; - response.Items.Add(new ResponseItemModel - { - Data = order, - Errors = errors - }); - } - - return response; - } - - /// - /// Define open price based on order - /// - /// - protected virtual double? GetOpenPrice(OrderModel nextOrder) - { - var pointModel = nextOrder?.Transaction?.Instrument?.Points?.LastOrDefault(); - - if (pointModel is not null) - { - switch (nextOrder?.Side) - { - case OrderSideEnum.Buy: return pointModel.Ask; - case OrderSideEnum.Sell: return pointModel.Bid; - } - } - - return null; - } - - /// - /// Update points - /// - /// - protected virtual IList CorrectAccounts(params IAccount[] accounts) - { - foreach (var account in accounts) - { - account.InitialBalance = account.Balance; - } - - return accounts; - } - - /// - /// Update points - /// - /// - protected virtual IList CorrectPoints(params PointModel[] points) - { - foreach (var point in points) - { - var instrument = Account.Instruments[point.Instrument.Name]; - var estimates = Account.ActivePositions.Select(o => o.Value.GainLossEstimate).ToList(); - - point.Instrument = instrument; - point.TimeFrame = instrument.TimeFrame; - - instrument.Points.Add(point); - instrument.PointGroups.Add(point, instrument.TimeFrame, true); - } - - return points; - } - } -} diff --git a/Core/Enums/EnvironmentEnum.cs b/Core/Enums/EnvironmentEnum.cs index 22b467d86..a9e9e39b5 100644 --- a/Core/Enums/EnvironmentEnum.cs +++ b/Core/Enums/EnvironmentEnum.cs @@ -4,6 +4,6 @@ public enum EnvironmentEnum : byte { None = 0, Live = 1, - Paper = 2 + Sandbox = 2 } } diff --git a/Core/Extensions/Dictionary.cs b/Core/Extensions/Dictionary.cs index 3212760ac..da89b39e4 100644 --- a/Core/Extensions/Dictionary.cs +++ b/Core/Extensions/Dictionary.cs @@ -7,7 +7,7 @@ public static class DictionaryExtensions { public static V Get(this IDictionary input, K index) { - return input.TryGetValue(index, out var value) ? value : default; + return index is not null && input.TryGetValue(index, out var value) ? value : default; } } } diff --git a/Core/Indicators/MovingAverageIndicator.cs b/Core/Indicators/MovingAverageIndicator.cs index 49d1c7836..864a4f5f5 100644 --- a/Core/Indicators/MovingAverageIndicator.cs +++ b/Core/Indicators/MovingAverageIndicator.cs @@ -1,3 +1,4 @@ +using Distribution.Services; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; diff --git a/Core/Indicators/RelativeStrengthIndicator.cs b/Core/Indicators/RelativeStrengthIndicator.cs index e88265a27..d5e5cd4bd 100644 --- a/Core/Indicators/RelativeStrengthIndicator.cs +++ b/Core/Indicators/RelativeStrengthIndicator.cs @@ -1,3 +1,4 @@ +using Distribution.Services; using System; using System.Collections.Generic; using System.Collections.ObjectModel; diff --git a/Core/Indicators/ScaleIndicator.cs b/Core/Indicators/ScaleIndicator.cs index 9395cd4c5..574d29849 100644 --- a/Core/Indicators/ScaleIndicator.cs +++ b/Core/Indicators/ScaleIndicator.cs @@ -1,3 +1,4 @@ +using Distribution.Services; using System; using System.Collections.Generic; using System.Collections.ObjectModel; diff --git a/Core/Services/InstanceService.cs b/Core/Services/InstanceService.cs deleted file mode 100644 index 08725b915..000000000 --- a/Core/Services/InstanceService.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace Terminal.Core.Services -{ - /// - /// Service to track account changes, including equity and quotes - /// - public class InstanceService where T: new() - { - private static readonly T _instance = new(); - - /// - /// Single instance - /// - public static T Instance => _instance; - - /// - /// Constructor - /// - static InstanceService() - { - } - - /// - /// Constructor - /// - private InstanceService() - { - } - } -} diff --git a/Core/Validators/Validators.cs b/Core/Validators/Validators.cs index 664ae140e..9c47cff4c 100644 --- a/Core/Validators/Validators.cs +++ b/Core/Validators/Validators.cs @@ -1,3 +1,4 @@ +using Distribution.Services; using FluentValidation; using System.Collections.Generic; using Terminal.Core.Services; diff --git a/Data/Options/GOOG b/Data/Options/GOOG deleted file mode 100644 index 5737ea0cd..000000000 Binary files a/Data/Options/GOOG and /dev/null differ diff --git a/Gateways/Simulation/Libs/Adapter.cs b/Gateways/Simulation/Libs/Adapter.cs index 1ea7c11e8..30ece8885 100644 --- a/Gateways/Simulation/Libs/Adapter.cs +++ b/Gateways/Simulation/Libs/Adapter.cs @@ -1,21 +1,20 @@ -using FluentValidation.Results; +using Distribution.Services; using Mapper; using System; using System.Collections.Generic; using System.Collections.Specialized; using System.IO; using System.Linq; -using System.Reactive.Concurrency; -using System.Reactive.Linq; using System.Threading.Tasks; +using System.Timers; using Terminal.Core.Domains; using Terminal.Core.Enums; using Terminal.Core.Extensions; using Terminal.Core.Models; -namespace Terminal.Connector.Simulation +namespace Simulation { - public class Adapter : Core.Domains.Connector, IDisposable + public class Adapter : Gateway, IDisposable { /// /// Disposable connections @@ -25,7 +24,7 @@ public class Adapter : Core.Domains.Connector, IDisposable /// /// Disposable subscriptions /// - protected IList _subscriptions; + protected IDictionary _subscriptions; /// /// Instrument streams @@ -42,16 +41,18 @@ public class Adapter : Core.Domains.Connector, IDisposable /// public virtual string Source { get; set; } + public virtual ScheduleService Scheduler { get; set; } = new(); + /// /// Constructor /// public Adapter() { - Speed = 100; + Speed = 1000; _connections = new List(); - _subscriptions = new List(); _instruments = new Dictionary(); + _subscriptions = new Dictionary(); } /// @@ -61,7 +62,7 @@ public override async Task> Connect() { await Disconnect(); - CorrectAccounts(Account); + SetupAccounts(Account); _instruments = Account .Instruments @@ -69,7 +70,8 @@ public override async Task> Connect() _instruments.ForEach(o => _connections.Add(o.Value)); - await Subscribe(); + Account.Positions.CollectionChanged += OnPositionUpdate; + Account.Instruments.ForEach(async o => await Subscribe(o.Key)); return null; } @@ -77,30 +79,32 @@ public override async Task> Connect() /// /// Subscribe to data streams /// - public override async Task> Subscribe() + public override async Task> Subscribe(string name) { - await Unsubscribe(); + await Unsubscribe(name); - OrderStream += OnOrderUpdate; - Account.Positions.CollectionChanged += OnPositionUpdate; - Account.Instruments.ForEach(o => o.Value.Points.CollectionChanged += OnPointUpdate); + Account.Instruments[name].Points.CollectionChanged += OnPointUpdate; - var span = TimeSpan.FromMilliseconds(Speed); + var span = TimeSpan.FromMicroseconds(Speed); var points = new Dictionary(); - var scheduler = new EventLoopScheduler(); - var interval = Observable - .Interval(span, scheduler) - .Subscribe(o => + var scheduler = InstanceService.Instance; + var interval = new Timer(span); + + interval.Enabled = true; + interval.AutoReset = true; + interval.Elapsed += (sender, e) => scheduler.Send(() => + { + var point = GetRecord(_instruments, points); + + if (point is not null) { - var point = GetRecord(_instruments, points); + SetupPoints(point); + } - if (point is not null) - { - CorrectPoints(point); - } - }); + interval.Enabled = true; + }); - _subscriptions.Add(interval); + _subscriptions[name] = interval; return null; } @@ -110,7 +114,8 @@ public override async Task> Subscribe() /// public override Task> Disconnect() { - Unsubscribe(); + Account.Instruments.ForEach(async o => await Unsubscribe(o.Key)); + Account.Positions.CollectionChanged -= OnPositionUpdate; _connections?.ForEach(o => o.Dispose()); _connections?.Clear(); @@ -121,14 +126,15 @@ public override Task> Disconnect() /// /// Unsubscribe from data streams /// - public override Task> Unsubscribe() + public override Task> Unsubscribe(string name) { - OrderStream -= OnOrderUpdate; - Account.Positions.CollectionChanged -= OnPositionUpdate; - Account.Instruments.ForEach(o => o.Value.Points.CollectionChanged -= OnPointUpdate); + Account.Instruments[name].Points.CollectionChanged -= OnPointUpdate; - _subscriptions?.ForEach(o => o.Dispose()); - _subscriptions?.Clear(); + if (_subscriptions.ContainsKey(name)) + { + _subscriptions[name].Dispose(); + _subscriptions.Remove(name); + } return Task.FromResult>(null); } @@ -149,7 +155,7 @@ public override Task> GetPoint(PointMessageModel m /// Create order and depending on the account, send it to the processing queue /// /// - public override Task> CreateOrders(params OrderModel[] orders) + public override Task> CreateOrders(params OrderModel[] orders) { var response = ValidateOrders(CorrectOrders(orders).ToArray()); @@ -176,7 +182,7 @@ public override Task> CreateOrders(params OrderModel[] /// Update orders /// /// - public override Task> UpdateOrders(params OrderModel[] orders) + public override Task> UpdateOrders(params OrderModel[] orders) { var nextOrders = orders.Select(nextOrder => { @@ -205,9 +211,9 @@ public override Task> UpdateOrders(params OrderModel[] /// Recursively cancel orders /// /// - public override Task> DeleteOrders(params OrderModel[] orders) + public override Task> DeleteOrders(params OrderModel[] orders) { - var response = new ResponseModel(); + var response = new ResponseMapModel(); foreach (var nextOrder in orders) { diff --git a/Gateways/Simulation/Libs/Simulation.csproj b/Gateways/Simulation/Libs/Simulation.csproj index f35f5b016..b143c9e31 100644 --- a/Gateways/Simulation/Libs/Simulation.csproj +++ b/Gateways/Simulation/Libs/Simulation.csproj @@ -13,6 +13,10 @@ MIT + + + + diff --git a/Gateways/Simulation/Tests/Connectors.cs b/Gateways/Simulation/Tests/Connectors.cs index b8c58bf8c..9d43c453e 100644 --- a/Gateways/Simulation/Tests/Connectors.cs +++ b/Gateways/Simulation/Tests/Connectors.cs @@ -1,10 +1,6 @@ +using Simulation; using System; -using System.Collections.Generic; -using System.Linq; -using Terminal.Connector.Simulation; using Terminal.Core.Domains; -using Terminal.Core.Enums; -using Terminal.Core.Models; namespace Terminal.Tests { @@ -21,374 +17,5 @@ public Connectors() Balance = 50000 }; } - - //[Theory] - //[InlineData(OrderTypeEnum.Stop)] - //[InlineData(OrderTypeEnum.Limit)] - //[InlineData(OrderTypeEnum.StopLimit)] - //public void ValidateOrdersWithoutOpenPrice(OrderTypeEnum orderType) - //{ - // var order = new OrderModel - // { - // Type = orderType - // }; - - // var error = "NotEmptyValidator"; - // var errors = base.ValidateOrders(order).Select(o => $"{o.PropertyName} {o.ErrorCode}"); - - // Assert.Contains($"{nameof(order.Transaction.Price)} {error}", errors); - //} - - //[Theory] - //[InlineData(OrderSideEnum.Buy, OrderTypeEnum.Stop, 5.0, 10.0, "GreaterThanOrEqualValidator")] - //[InlineData(OrderSideEnum.Sell, OrderTypeEnum.Stop, 10.0, 5.0, "LessThanOrEqualValidator")] - //[InlineData(OrderSideEnum.Buy, OrderTypeEnum.Limit, 10.0, 5.0, "LessThanOrEqualValidator")] - //[InlineData(OrderSideEnum.Sell, OrderTypeEnum.Limit, 5.0, 10.0, "GreaterThanOrEqualValidator")] - //public void ValidateOrdersWithIncorrectPrice( - // OrderSideEnum orderSide, - // OrderTypeEnum orderType, - // double orderPrice, - // double price, - // string error) - //{ - // var order = GenerateOrder(AssetX, orderSide, orderType, 1.0, price, price, null, orderPrice); - // var errors = base.ValidateOrders(order).Select(o => $"{o.PropertyName} {o.ErrorCode}"); - - // Assert.Contains($"{nameof(order.Transaction.Price)} {error}", errors); - //} - - //[Theory] - //[InlineData(OrderSideEnum.Buy, 15.0, null, 10.0, "NotEmptyValidator", "GreaterThanOrEqualValidator")] - //[InlineData(OrderSideEnum.Sell, 15.0, null, 5.0, "NotEmptyValidator", "LessThanOrEqualValidator")] - //[InlineData(OrderSideEnum.Buy, 5.0, 10.0, 15.0, "GreaterThanOrEqualValidator", "GreaterThanOrEqualValidator")] - //[InlineData(OrderSideEnum.Sell, 15.0, 10.0, 5.0, "LessThanOrEqualValidator", "LessThanOrEqualValidator")] - //public void ValidateOrdersWithIncorrectStopLimitPrice( - // OrderSideEnum orderSide, - // double? orderPrice, - // double? activationPrice, - // double? price, - // string activationError, - // string orderError) - //{ - // var order = GenerateOrder(AssetX, orderSide, OrderTypeEnum.StopLimit, 1.0, price, price, activationPrice, orderPrice); - // var errors = base.ValidateOrders(order).Select(o => $"{o.PropertyName} {o.ErrorCode}"); - - // Assert.Contains($"{nameof(order.ActivationPrice)} {activationError}", errors); - // Assert.Contains($"{nameof(order.Transaction.Price)} {orderError}", errors); - //} - - //[Fact] - //public void CreateOrdersWithEmptyOrder() - //{ - // var order = new OrderModel(); - - // base.CreateOrders(order); - - // Assert.Empty(Account.Orders); - // Assert.Empty(Account.Positions); - // Assert.Empty(Account.ActiveOrders); - // Assert.Empty(Account.ActivePositions); - // Assert.Null(order.Transaction.Status); - //} - - //[Theory] - //[InlineData(OrderSideEnum.Buy, OrderTypeEnum.Market, 5.0, null, null, 1, 0, 0, 1)] - //[InlineData(OrderSideEnum.Sell, OrderTypeEnum.Market, 5.0, null, null, 1, 0, 0, 1)] - //[InlineData(OrderSideEnum.Buy, OrderTypeEnum.Stop, 5.0, null, 15.0, 1, 1, 0, 0)] - //[InlineData(OrderSideEnum.Sell, OrderTypeEnum.Stop, 15.0, null, 5.0, 1, 1, 0, 0)] - //[InlineData(OrderSideEnum.Buy, OrderTypeEnum.Limit, 15.0, null, 5.0, 1, 1, 0, 0)] - //[InlineData(OrderSideEnum.Sell, OrderTypeEnum.Limit, 5.0, null, 15.0, 1, 1, 0, 0)] - //[InlineData(OrderSideEnum.Buy, OrderTypeEnum.StopLimit, 5.0, 10.0, 15.0, 1, 1, 0, 0)] - //[InlineData(OrderSideEnum.Sell, OrderTypeEnum.StopLimit, 15.0, 10.0, 5.0, 1, 1, 0, 0)] - //public void CreateOrdersWithoutMatching( - // OrderSideEnum orderSide, - // OrderTypeEnum orderType, - // double? price, - // double? activationPrice, - // double? orderPrice, - // int orders, - // int activeOrders, - // int positions, - // int activePositions) - //{ - // var order = GenerateOrder(AssetX, orderSide, orderType, 1.0, price, price, activationPrice, orderPrice); - - // base.CreateOrders(order); - - // Assert.Equal(orders, Account.Orders.Count); - // Assert.Equal(activeOrders, Account.ActiveOrders.Count); - // Assert.Equal(positions, Account.Positions.Count); - // Assert.Equal(activePositions, Account.ActivePositions.Count); - //} - - //[Theory] - //[InlineData(OrderSideEnum.Buy, OrderTypeEnum.Stop, 15.0, null, 25.0)] - //[InlineData(OrderSideEnum.Sell, OrderTypeEnum.Stop, 15.0, null, 5.0)] - //[InlineData(OrderSideEnum.Buy, OrderTypeEnum.Limit, 15.0, null, 5.0)] - //[InlineData(OrderSideEnum.Sell, OrderTypeEnum.Limit, 15.0, null, 25.0)] - //[InlineData(OrderSideEnum.Buy, OrderTypeEnum.StopLimit, 15.0, 20.0, 25.0)] - //[InlineData(OrderSideEnum.Sell, OrderTypeEnum.StopLimit, 15.0, 10.0, 5.0)] - //public void SendPendingOrderUpdatingStatements( - // OrderSideEnum orderSide, - // OrderTypeEnum orderType, - // double? price, - // double? activationPrice, - // double? orderPrice) - //{ - // var order = GenerateOrder(AssetX, orderSide, orderType, 1.0, price, price, activationPrice, orderPrice); - // var orderId = order.Transaction.Descriptor.Id; - - // base.SendPendingOrder(order); - - // Assert.Equal(order.Transaction.Status, OrderStatusEnum.Placed); - // Assert.Equal(order, Account.Orders[0]); - // Assert.Equal(order, Account.ActiveOrders[orderId]); - // Assert.Single(Account.Orders); - // Assert.Single(Account.ActiveOrders); - // Assert.Empty(Account.Positions); - // Assert.Empty(Account.ActivePositions); - //} - - //[Theory] - //[InlineData(OrderSideEnum.Buy, OrderTypeEnum.Market, 10.0, 15.0, 15.0)] - //[InlineData(OrderSideEnum.Sell, OrderTypeEnum.Market, 10.0, 15.0, 10.0)] - //public void CreatePositionWithoutMatching( - // OrderSideEnum orderSide, - // OrderTypeEnum orderType, - // double? bid, - // double? ask, - // double? price) - //{ - // var order = GenerateOrder(AssetX, orderSide, orderType, 1.0, bid, ask, null, null); - // var point = order.Transaction.Instrument.Points.First(); - // var orderId = order.Transaction.Descriptor.Id; - - // base.CreatePosition(order); - - // var position = Account.ActivePositions[orderId]; - // var openPrice = position.Orders.First(); - // var closePrice = position.Orders.Last(); - - // Assert.Equal(order.Transaction.Price, price); - // Assert.Equal(order.Transaction.Status, OrderStatusEnum.Filled); - - // Assert.Equal(position.Order.Transaction.Time, order.Transaction.Time); - // Assert.Equal(position.Order.Transaction.Price, price); - // Assert.Equal(openPrice.Transaction.Price, price); - // Assert.Equal(closePrice.Transaction.Price, price); - // Assert.Single(position.Orders); - - // Assert.Equal(order, Account.Orders[0]); - // Assert.Equal(orderId, Account.ActivePositions[orderId].Order.Transaction.Descriptor.Id); - // Assert.Empty(Account.Positions); - // Assert.Empty(Account.ActiveOrders); - // Assert.Single(Account.Orders); - // Assert.Single(Account.ActivePositions); - //} - - //[Fact] - //public void CreatePositionWithPendingOrders() - //{ - // var price = 15.0; - // var order = GenerateOrder(AssetX, OrderSideEnum.Buy, OrderTypeEnum.Market, 1.0, price, price, null, null); - // var SL = GenerateOrder(AssetX, OrderSideEnum.Sell, OrderTypeEnum.Stop, 1.0, price, price, null, 5.0); - // var TP = GenerateOrder(AssetX, OrderSideEnum.Sell, OrderTypeEnum.Limit, 1.0, price, price, null, 25.0); - // var orderId = order.Transaction.Descriptor.Id; - - // order.Orders.Add(SL); - // order.Orders.Add(TP); - - // var position = base.CreatePosition(order); - - // Assert.Empty(Account.Positions); - // Assert.Empty(Account.ActiveOrders); - // Assert.Single(Account.Orders); - // Assert.Single(Account.ActivePositions); - // Assert.Equal(order, Account.Orders[0]); - // Assert.Equal(orderId, Account.ActivePositions[orderId].Order.Transaction.Descriptor.Id); - // Assert.Equal(position, Account.ActivePositions[orderId]); - //} - - //[Fact] - //public void GetPositionFromOrder() - //{ - // var SL = GenerateOrder(AssetX, OrderSideEnum.Sell, OrderTypeEnum.Stop, 2.0, 15.0, 15.0, 5.0, null); - // var order = GenerateOrder(AssetX, OrderSideEnum.Buy, OrderTypeEnum.Stop, 1.0, 15.0, 15.0, null, 25.0); - // var orderId = order.Transaction.Descriptor.Id; - - // order.Orders.Add(SL); - - // var response = base.GetPosition(order); - - // Assert.Equal(orderId, response.Order.Transaction.Descriptor.Id); - // Assert.Equal(order.Transaction.Descriptor.Name, response.Order.Transaction.Descriptor.Name); - // Assert.Equal(order.Transaction.Descriptor.Description, response.Order.Transaction.Descriptor.Description); - // Assert.Equal(order.Transaction.Descriptor.Group, response.Order.Transaction.Descriptor.Group); - // Assert.Equal(order.Type, response.Order.Type); - // Assert.Equal(order.Side, response.Order.Side); - // Assert.Equal(order.Transaction.Volume, response.Order.Transaction.Volume); - // Assert.Equal(order.Transaction.Price, response.Order.Transaction.Price); - // Assert.Equal(order.Transaction.Instrument, response.Order.Transaction.Instrument); - // Assert.Equal(order.ActivationPrice, response.Order.ActivationPrice); - // Assert.Equal(order.Orders, response.Orders); - //} - - //[Fact] - //public void UpdateOrdersWithMatches() - //{ - // var SL = GenerateOrder(AssetX, OrderSideEnum.Sell, OrderTypeEnum.Stop, 2.0, 15.0, 15.0, 5.0, null); - // var order = GenerateOrder(AssetX, OrderSideEnum.Buy, OrderTypeEnum.StopLimit, 2.0, 5.0, 5.0, 10.0, 15.0); - // var orderX = GenerateOrder(AssetX, OrderSideEnum.Buy, OrderTypeEnum.Stop, 1.0, 15.0, 15.0, null, 25.0); - // var orderY = GenerateOrder(AssetX, OrderSideEnum.Sell, OrderTypeEnum.Stop, 1.0, 15.0, 15.0, null, 5.0); - - // order.Transaction.Descriptor.Id = orderY.Transaction.Descriptor.Id; - // order.Orders.Add(SL); - - // var orderId = order.Transaction.Descriptor.Id; - // var orderIdX = orderX.Transaction.Descriptor.Id; - // var orderIdY = orderY.Transaction.Descriptor.Id; - - // Account.ActiveOrders.Add(orderIdX, orderX); - // Account.ActiveOrders.Add(orderIdY, orderY); - - // base.UpdateOrders(order); - - // var update = Account.ActiveOrders[orderIdY]; - - // Assert.Equal(2, Account.ActiveOrders.Count); - // Assert.Equal(orderIdY, Account.ActiveOrders[orderIdY].Transaction.Descriptor.Id); - // Assert.Equal(orderIdX, Account.ActiveOrders[orderIdX].Transaction.Descriptor.Id); - // Assert.Equal(orderId, update.Transaction.Descriptor.Id); - // Assert.Equal(order.Transaction.Descriptor.Name, update.Transaction.Descriptor.Name); - // Assert.Equal(order.Transaction.Descriptor.Description, update.Transaction.Descriptor.Description); - // Assert.Equal(order.Transaction.Descriptor.Group, update.Transaction.Descriptor.Group); - // Assert.Equal(order.Type, update.Type); - // Assert.Equal(order.Side, update.Side); - // Assert.Equal(order.Transaction.Volume, update.Transaction.Volume); - // Assert.Equal(order.Transaction.Price, update.Transaction.Price); - // Assert.Equal(order.Transaction.Instrument, update.Transaction.Instrument); - // Assert.Equal(order.ActivationPrice, update.ActivationPrice); - // Assert.Equal(order.Orders, update.Orders); - // Assert.Empty(Account.ActivePositions); - // Assert.Empty(Account.Positions); - // Assert.Empty(Account.Orders); - //} - - //[Fact] - //public void IncreasePositionWithMatches() - //{ - // var instrumentX = Account.Instruments[AssetX]; - // var instrumentY = Account.Instruments[AssetY]; - // var pointX = instrumentX.Points.Last(); - // var pointY = instrumentY.Points.Last(); - // var orderX = GenerateOrder(AssetX, OrderSideEnum.Buy, OrderTypeEnum.Market, 1, pointX.Bid, pointX.Ask, null, null); - // var orderY = GenerateOrder(AssetY, OrderSideEnum.Buy, OrderTypeEnum.Market, 2, pointY.Bid, pointY.Ask, null, null); - // var increase = GenerateOrder(AssetY, OrderSideEnum.Buy, OrderTypeEnum.Market, 3, pointY.Bid + 5, pointY.Ask + 5, null, null); - - // // Open - - // base.CreateOrders(orderX); - // base.CreateOrders(orderY); - - // // Increase - - // var previousPosition = Account.ActivePositions[orderY.Transaction.Descriptor.Id]; - // var nextPosition = base.IncreasePosition(increase, previousPosition); - // var averageTradePrice = nextPosition.Order.Orders.Sum(o => o.Transaction.Volume * o.Transaction.Price) / nextPosition.Orders.Sum(o => o.Transaction.Volume); - - // Assert.Equal(3, Account.Orders.Count); - // Assert.Equal(0, Account.ActiveOrders.Count); - // Assert.Equal(2, Account.ActivePositions.Count); - // Assert.Empty(Account.Positions); - - // var openA = nextPosition.Orders[0]; - // var openB = nextPosition.Orders[1]; - - // Assert.Equal(orderY.Transaction.Volume, openA.Transaction.Volume); - // Assert.Equal(increase.Transaction.Volume, openB.Transaction.Volume); - // Assert.Equal(pointY.Ask, openA.Transaction.Price); - // Assert.Equal(pointY.Ask + 5, openB.Transaction.Price); - // Assert.Equal(orderY.Transaction.Time, openA.Transaction.Time); - // Assert.Equal(increase.Transaction.Time, openB.Transaction.Time); - // Assert.Equal(2, nextPosition.Orders.Count); - - // Assert.Equal(increase.Transaction.Descriptor.Id, nextPosition.Order.Transaction.Descriptor.Id); - // Assert.Equal(increase.Transaction.Time, nextPosition.Order.Transaction.Time); - // Assert.Equal(averageTradePrice, nextPosition.Order.Transaction.Price); - // Assert.Equal(increase.Transaction.Volume + orderY.Transaction.Volume, nextPosition.Order.Transaction.Volume); - - // Assert.Equal(nextPosition.Order.Transaction.Time, previousPosition.Order.Transaction.Time); - // Assert.Equal(openB.Transaction.Price, previousPosition.Order.Transaction.Price); - // Assert.Equal(previousPosition.GainLossEstimate, previousPosition.GainLoss); - // Assert.Equal(previousPosition.GainLossPointsEstimate, previousPosition.GainLossPoints); - - // // Estimate - - // var step = instrumentY.StepValue / instrumentY.StepSize; - // var priceUpdate = new PointModel { Ask = 50, Bid = 40, Last = 40, Instrument = instrumentY }; - - // instrumentY.Points.Add(priceUpdate); - // instrumentY.PointGroups.Add(priceUpdate); - - // nextPosition.Order.Transaction.Instrument = instrumentY; - - // Assert.Equal(nextPosition.GainLossPointsEstimate * nextPosition.Order.Transaction.Volume * step - instrumentY.Commission, nextPosition.GainLossEstimate); - // Assert.Equal(priceUpdate.Bid - nextPosition.Order.Transaction.Price, nextPosition.GainLossPointsEstimate); - //} - - //[Fact] - //public void DecreasePositionWithMatches() - //{ - // var instrumentX = Account.Instruments[AssetX]; - // var instrumentY = Account.Instruments[AssetY]; - // var pointX = instrumentX.Points.Last(); - // var pointY = instrumentY.Points.Last(); - // var orderX = GenerateOrder(AssetX, OrderSideEnum.Buy, OrderTypeEnum.Market, 1, pointX.Bid, pointX.Ask, null, null); - // var orderY = GenerateOrder(AssetY, OrderSideEnum.Buy, OrderTypeEnum.Market, 1, pointY.Bid, pointY.Ask, null, null); - // var decreaseA = GenerateOrder(AssetY, OrderSideEnum.Sell, OrderTypeEnum.Market, 2, pointY.Bid + 5, pointY.Ask + 5, null, null); - // var decreaseB = GenerateOrder(AssetY, OrderSideEnum.Buy, OrderTypeEnum.Market, 1, pointY.Bid + 15, pointY.Ask + 15, null, null); - - // // Open - - // base.CreateOrders(orderX); - // base.CreateOrders(orderY); - - // // Inverse - - // var previousPosition = Account.ActivePositions[orderY.Transaction.Descriptor.Id]; - // var nextPosition = base.DecreasePosition(decreaseA, previousPosition); - - // Assert.Equal(3, Account.Orders.Count); - // Assert.Equal(0, Account.ActiveOrders.Count); - // Assert.Equal(2, Account.ActivePositions.Count); - // Assert.Single(Account.Positions); - - // var openA = nextPosition.Orders[0]; - - // Assert.Equal(decreaseA.Transaction.Volume, openA.Transaction.Volume); - // Assert.Equal(pointY.Bid + 5, openA.Transaction.Price); - // Assert.Equal(decreaseA.Transaction.Time, openA.Transaction.Time); - // Assert.Single(nextPosition.Orders); - - // Assert.Equal(decreaseA.Transaction.Descriptor.Id, nextPosition.Order.Transaction.Descriptor.Id); - // Assert.Equal(decreaseA.Transaction.Time, nextPosition.Order.Transaction.Time); - // Assert.Equal(openA.Transaction.Price, nextPosition.Order.Transaction.Price); - // Assert.Equal(Math.Abs(orderY.Transaction.Volume.Value - decreaseA.Transaction.Volume.Value), nextPosition.Order.Transaction.Volume); - - // Assert.Equal(nextPosition.Order.Transaction.Time, previousPosition.Order.Transaction.Time); - // Assert.Equal(openA.Transaction.Price, previousPosition.Order.Transaction.Price); - // Assert.Equal(previousPosition.GainLossEstimate, previousPosition.GainLoss); - // Assert.Equal(previousPosition.GainLossPointsEstimate, previousPosition.GainLossPoints); - - // // Close - - // previousPosition = Account.ActivePositions[decreaseA.Transaction.Descriptor.Id]; - // nextPosition = base.DecreasePosition(decreaseB, previousPosition); - - // Assert.Equal(4, Account.Orders.Count); - // Assert.Equal(0, Account.ActiveOrders.Count); - // Assert.Equal(2, Account.Positions.Count); - // Assert.Equal(1, Account.ActivePositions.Count); - //} } } diff --git a/Gateways/Simulation/Tests/CreateOrders.cs b/Gateways/Simulation/Tests/CreateOrders.cs index 8b76a3c61..b298b6e5f 100644 --- a/Gateways/Simulation/Tests/CreateOrders.cs +++ b/Gateways/Simulation/Tests/CreateOrders.cs @@ -1,5 +1,5 @@ using System; -using Terminal.Connector.Simulation; +using Simulation; using Terminal.Core.Collections; using Terminal.Core.Domains; using Terminal.Core.Enums; diff --git a/Gateways/Simulation/Tests/CreatePosition.cs b/Gateways/Simulation/Tests/CreatePosition.cs index 0f3047498..ae0d78380 100644 --- a/Gateways/Simulation/Tests/CreatePosition.cs +++ b/Gateways/Simulation/Tests/CreatePosition.cs @@ -1,6 +1,6 @@ using System; using System.Linq; -using Terminal.Connector.Simulation; +using Simulation; using Terminal.Core.Collections; using Terminal.Core.Domains; using Terminal.Core.Enums; diff --git a/Gateways/Simulation/Tests/DecreasePosition.cs b/Gateways/Simulation/Tests/DecreasePosition.cs index abbe9bb66..1b448f57d 100644 --- a/Gateways/Simulation/Tests/DecreasePosition.cs +++ b/Gateways/Simulation/Tests/DecreasePosition.cs @@ -1,6 +1,6 @@ using System; using System.Linq; -using Terminal.Connector.Simulation; +using Simulation; using Terminal.Core.Collections; using Terminal.Core.Domains; using Terminal.Core.Enums; @@ -35,8 +35,8 @@ public void Decrease() var nextPosition = base.DecreasePosition(order, previousPosition); Assert.Single(Account.Positions); + Assert.Empty(Account.ActiveOrders); Assert.Equal(3, Account.Orders.Count); - Assert.Equal(0, Account.ActiveOrders.Count); Assert.Equal(2, Account.ActivePositions.Count); Assert.Equal(2, nextPosition.Orders.Count); @@ -87,9 +87,9 @@ public void Close() var nextPosition = base.DecreasePosition(order, previousPosition); Assert.Single(Account.Positions); + Assert.Single(Account.ActivePositions); Assert.Equal(3, Account.Orders.Count); - Assert.Equal(0, Account.ActiveOrders.Count); - Assert.Equal(1, Account.ActivePositions.Count); + Assert.Empty(Account.ActiveOrders); Assert.Equal(2, nextPosition.Orders.Count); // Order #1 @@ -139,8 +139,8 @@ public void Inverse() var nextPosition = base.DecreasePosition(order, previousPosition); Assert.Single(Account.Positions); + Assert.Empty(Account.ActiveOrders); Assert.Equal(3, Account.Orders.Count); - Assert.Equal(0, Account.ActiveOrders.Count); Assert.Equal(2, Account.ActivePositions.Count); Assert.Equal(2, nextPosition.Orders.Count); diff --git a/Gateways/Simulation/Tests/IncreasePosition.cs b/Gateways/Simulation/Tests/IncreasePosition.cs index 48a601178..7a9d09592 100644 --- a/Gateways/Simulation/Tests/IncreasePosition.cs +++ b/Gateways/Simulation/Tests/IncreasePosition.cs @@ -1,6 +1,6 @@ using System; using System.Linq; -using Terminal.Connector.Simulation; +using Simulation; using Terminal.Core.Collections; using Terminal.Core.Domains; using Terminal.Core.Enums; @@ -42,8 +42,8 @@ public void Increase() // State Assert.Empty(Account.Positions); + Assert.Empty(Account.ActiveOrders); Assert.Equal(3, Account.Orders.Count); - Assert.Equal(0, Account.ActiveOrders.Count); Assert.Equal(2, Account.ActivePositions.Count); Assert.Equal(2, nextPosition.Orders.Count); diff --git a/Gateways/Simulation/Tests/SendPendingOrder.cs b/Gateways/Simulation/Tests/SendPendingOrder.cs index 102c3ffb7..2932cf40c 100644 --- a/Gateways/Simulation/Tests/SendPendingOrder.cs +++ b/Gateways/Simulation/Tests/SendPendingOrder.cs @@ -1,5 +1,5 @@ using System; -using Terminal.Connector.Simulation; +using Simulation; using Terminal.Core.Collections; using Terminal.Core.Domains; using Terminal.Core.Enums; diff --git a/Gateways/Simulation/Tests/Terminal.Tests.csproj b/Gateways/Simulation/Tests/Terminal.Tests.csproj deleted file mode 100644 index de9b75f60..000000000 --- a/Gateways/Simulation/Tests/Terminal.Tests.csproj +++ /dev/null @@ -1,28 +0,0 @@ - - - - net7.0 - disable - disable - false - - - - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - - - - - diff --git a/Gateways/Simulation/Tests/UpdateOrders.cs b/Gateways/Simulation/Tests/UpdateOrders.cs index b225bf10f..29dcb49cf 100644 --- a/Gateways/Simulation/Tests/UpdateOrders.cs +++ b/Gateways/Simulation/Tests/UpdateOrders.cs @@ -1,6 +1,6 @@ using System; using System.Linq; -using Terminal.Connector.Simulation; +using Simulation; using Terminal.Core.Collections; using Terminal.Core.Domains; using Terminal.Core.Enums; diff --git a/Gateways/Simulation/Tests/ValidateOrders.cs b/Gateways/Simulation/Tests/ValidateOrders.cs index 9bf47b024..beefc43e8 100644 --- a/Gateways/Simulation/Tests/ValidateOrders.cs +++ b/Gateways/Simulation/Tests/ValidateOrders.cs @@ -1,8 +1,7 @@ -using FluentValidation.Results; +using Simulation; using System; using System.Collections.Generic; using System.Linq; -using Terminal.Connector.Simulation; using Terminal.Core.Collections; using Terminal.Core.Domains; using Terminal.Core.Enums; diff --git a/Gateways/Tda/Libs/Adapter.cs b/Gateways/Tda/Libs/Adapter.cs deleted file mode 100644 index 48b53d991..000000000 --- a/Gateways/Tda/Libs/Adapter.cs +++ /dev/null @@ -1,255 +0,0 @@ -using ServiceScheduler; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net.Http; -using System.Reactive.Linq; -using System.Threading; -using System.Threading.Tasks; -using Terminal.Connector.Tda.Models; -using Terminal.Core.Enums; -using Terminal.Core.Extensions; -using Terminal.Core.Models; -using Terminal.Core.Services; - -namespace Terminal.Connector.Tda -{ - public class Adapter : Terminal.Core.Domains.Connector - { - /// - /// User - /// - protected UserModel _user; - - /// - /// Disposable connections - /// - protected IList _connections; - - /// - /// Disposable subscriptions - /// - protected IList _subscriptions; - - /// - /// Application ID - /// - public virtual string ConsumerKey { get; set; } - - /// - /// Username - /// - public virtual string Username { get; set; } - - /// - /// Password - /// - public virtual string Password { get; set; } - - /// - /// Answer to a secret question - /// - public virtual string Answer { get; set; } - - /// - /// Query URL - /// - public virtual string QueryUri { get; set; } = "https://api.tdameritrade.com/v1"; - - /// - /// Authentication URL - /// - public virtual string SignInRemoteUri { get; set; } = "https://auth.tdameritrade.com/auth"; - - /// - /// Authentication URL for API - /// - public virtual string SignInApiUri { get; set; } = "https://api.tdameritrade.com/v1/oauth2/token"; - - /// - /// Local return URL for authentication - /// - public virtual string SignInLocalUri { get; set; } = "http://localhost"; - - /// - /// Constructor - /// - public Adapter() - { - _connections = new List(); - _subscriptions = new List(); - } - - /// - /// Connect - /// - public override async Task> Connect() - { - await Disconnect(); - - var automator = new Authenticator - { - Location = Path.GetTempPath() - }; - - _user = await automator.SignIn(this); - - await Subscribe(); - - return null; - } - - /// - /// Subscribe to data streams - /// - public override async Task> Subscribe() - { - await Unsubscribe(); - - return null; - } - - /// - /// Save state and dispose - /// - public override Task> Disconnect() - { - Unsubscribe(); - - _connections?.ForEach(o => o.Dispose()); - _connections?.Clear(); - - return Task.FromResult>(null); - } - - /// - /// Unsubscribe from data streams - /// - public override Task> Unsubscribe() - { - _subscriptions?.ForEach(o => o.Dispose()); - _subscriptions?.Clear(); - - return Task.FromResult>(null); - } - - /// - /// Get quote - /// - /// - public override Task> GetPoint(PointMessageModel message) - { - var response = new ResponseItemModel - { - Data = Account.Instruments[message.Name].Points.LastOrDefault() - }; - - return Task.FromResult(response); - } - - /// - /// Get option chains - /// - /// - public override async Task>> GetOptions(OptionMessageModel message) - { - var props = new Dictionary - { - ["symbol"] = message.Name, - ["fromDate"] = $"{message.MinDate:yyyy-MM-dd}", - ["toDate"] = $"{message.MaxDate:yyyy-MM-dd}", - ["includeQuotes"] = "TRUE" - }; - - var query = new HttpRequestMessage - { - RequestUri = new Uri($"{QueryUri}/marketdata/chains?{props.ToQuery()}") - }; - - query.Headers.Add("Authorization", $"{_user.TokenType} {_user.AccessToken}"); - - var service = InstanceService.Instance; - var response = await service.Send(query, service.Options, new CancellationTokenSource()); - var options = response - .PutExpDateMap - .Concat(response.CallExpDateMap) - .SelectMany(dateMap => dateMap.Value.SelectMany(o => o.Value)) - .Select(o => - { - var option = new OptionModel - { - Name = o.Symbol, - BaseName = response.Symbol, - OpenInterest = o.OpenInterest ?? 0, - Strike = o.StrikePrice ?? 0, - IntrinsicValue = o.IntrinsicValue ?? 0, - Leverage = o.Multiplier ?? 0, - Volatility = o.Volatility ?? 0, - Volume = o.TotalVolume ?? 0, - Point = new PointModel - { - Ask = o.Ask ?? 0, - AskSize = o.AskSize ?? 0, - Bid = o.Bid ?? 0, - BidSize = o.BidSize ?? 0, - Bar = new BarModel - { - Low = o.LowPrice ?? 0, - High = o.LowPrice ?? 0, - Open = o.OpenPrice ?? 0, - Close = o.ClosePrice ?? 0 - } - }, - Derivatives = new DerivativeModel - { - Rho = o.Rho ?? 0, - Vega = o.Vega ?? 0, - Delta = o.Delta ?? 0, - Gamma = o.Gamma ?? 0, - Theta = o.Theta ?? 0 - } - }; - - switch (o.PutCall.ToUpper()) - { - case "PUT": option.Side = OptionSideEnum.Put; break; - case "CALL": option.Side = OptionSideEnum.Call; break; - } - - if (o.ExpirationDate is not null) - { - option.ExpirationDate = DateTimeOffset.FromUnixTimeMilliseconds(o.ExpirationDate.Value).UtcDateTime; - } - - return option; - - }).ToList(); - - return new ResponseItemModel> - { - Data = options - }; - } - - public override Task>> GetPoints(PointMessageModel message) - { - throw new NotImplementedException(); - } - - public override Task> CreateOrders(params OrderModel[] orders) - { - throw new NotImplementedException(); - } - - public override Task> UpdateOrders(params OrderModel[] orders) - { - throw new NotImplementedException(); - } - - public override Task> DeleteOrders(params OrderModel[] orders) - { - throw new NotImplementedException(); - } - } -} diff --git a/Gateways/Tda/Libs/Authenticator.cs b/Gateways/Tda/Libs/Authenticator.cs deleted file mode 100644 index 6358c40fe..000000000 --- a/Gateways/Tda/Libs/Authenticator.cs +++ /dev/null @@ -1,170 +0,0 @@ -using Microsoft.AspNetCore.Http.Extensions; -using PuppeteerSharp; -using ServiceScheduler; -using System; -using System.Collections.Generic; -using System.IO; -using System.Net.Http; -using System.Text.Json; -using System.Threading.Tasks; -using Terminal.Connector.Tda.Models; -using Terminal.Core.Extensions; -using Terminal.Core.Services; - -namespace Terminal.Connector.Tda -{ - public class Authenticator - { - /// - /// Keys - /// - public virtual string Location { get; set; } - - /// - /// Load keys - /// - /// - /// - public virtual T GetUser(string name) - { - var location = Path.Combine(Location, name); - - if (File.Exists(location)) - { - return File.ReadAllText(location).Deserialize(); - } - - return default; - } - - /// - /// Save keys - /// - /// - /// - public virtual void SaveUser(string name, T model) - { - File.WriteAllText(Path.Combine(Location, name), JsonSerializer.Serialize(model)); - } - - /// - /// Manual sign in - /// - /// - /// - public virtual async Task SignIn(Adapter adapter) - { - var user = GetUser(nameof(Authenticator)); - - if (user.ExpirationDate > DateTime.UtcNow.Ticks) - { - return user; - } - - using (var browserFetcher = new BrowserFetcher()) - { - await browserFetcher.DownloadAsync(BrowserFetcher.DefaultChromiumRevision); - - var browser = await Puppeteer.LaunchAsync(new LaunchOptions { Headless = false }); - var source = new UriBuilder(adapter.SignInRemoteUri); - var page = await browser.NewPageAsync(); - var nav = page.WaitForNavigationAsync(); - - source.Query = new QueryBuilder - { - { "response_type", "code" }, - { "redirect_uri", adapter.SignInLocalUri }, - { "client_id", $"{adapter.ConsumerKey}@AMER.OAUTHAP" } - } + string.Empty; - - page.Request += (sender, e) => - { - if (e.Request.ResourceType == ResourceType.StyleSheet) - { - e.Request.AbortAsync(); - return; - } - - e.Request.ContinueAsync(); - }; - - await page.SetRequestInterceptionAsync(true); - await page.GoToAsync($"{source}"); - await page.WaitForSelectorAsync(".accept.button"); - await page.TypeAsync("[name=su_username]", adapter.Username); - await page.TypeAsync("[name=su_password]", adapter.Password); - await page.ClickAsync(".accept.button"); - await nav; - - var verification = await page.WaitForSelectorAsync(".alternates .row"); - - if (verification is not null) - { - nav = page.WaitForNavigationAsync(); - - await page.ClickAsync(".alternates .row"); - await page.WaitForSelectorAsync("[name=init_secretquestion]"); - await page.ClickAsync("[name=init_secretquestion]"); - await nav; - - nav = page.WaitForNavigationAsync(); - - await page.WaitForSelectorAsync("[name=su_secretquestion]"); - await page.TypeAsync("[name=su_secretquestion]", adapter.Answer); - await page.ClickAsync(".accept.button"); - await nav; - - nav = page.WaitForNavigationAsync(); - - await page.WaitForSelectorAsync("[name=su_trustthisdevice]"); - await page.ClickAsync("[name=su_trustthisdevice]"); - await page.WaitForSelectorAsync("input[type='radio']:checked"); - await page.ClickAsync(".accept.button"); - await nav; - } - - nav = page.WaitForNavigationAsync(); - - await page.ClickAsync(".accept.button"); - await nav; - - var codeSource = page.Target.Url; - var securityCode = codeSource.ToPairs().Get("code"); - var userModel = await Authenticate(adapter, securityCode); - - userModel.SecurityCode = securityCode; - userModel.ConsumerKey = adapter.ConsumerKey; - userModel.ExpirationDate = DateTime.UtcNow.AddSeconds(userModel.ExpiresIn).Ticks; - - SaveUser(nameof(Authenticator), userModel); - - return userModel; - } - } - - /// - /// Get API token - /// - /// - /// - /// - public async Task Authenticate(Adapter adapter, string securityCode) - { - var props = new Dictionary - { - { "grant_type", "authorization_code" }, - { "access_type", "offline" }, - { "client_id", $"{adapter.ConsumerKey}@AMER.OAUTHAP" }, - { "redirect_uri", adapter.SignInLocalUri }, - { "code", securityCode } - }; - - var query = new HttpRequestMessage(HttpMethod.Post, adapter.SignInApiUri) - { - Content = new FormUrlEncodedContent(props) - }; - - return await InstanceService.Instance.Send(query); - } - } -} diff --git a/Gateways/Tda/Libs/Extensions/DateTime.cs b/Gateways/Tda/Libs/Extensions/DateTime.cs deleted file mode 100644 index cd25b930e..000000000 --- a/Gateways/Tda/Libs/Extensions/DateTime.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; - -namespace Terminal.Core.Extensions -{ - public static class DateTimeExtensions - { - public static DateTime? Round(this DateTime? input, TimeSpan? span) - { - if (input is null) - { - return null; - } - - var date = input.Value.Ticks; - var excess = Math.Max(span?.Ticks ?? 1, 1); - - return new DateTime(date - (date % excess), input.Value.Kind); - } - } -} diff --git a/Gateways/Tda/Libs/Extensions/Dictionary.cs b/Gateways/Tda/Libs/Extensions/Dictionary.cs deleted file mode 100644 index 7f324a7e3..000000000 --- a/Gateways/Tda/Libs/Extensions/Dictionary.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Collections.Generic; -using System.Web; - -namespace Terminal.Core.Extensions -{ - public static class DictionaryExtensions - { - public static string ToQuery(this IDictionary input) - { - var inputs = HttpUtility.ParseQueryString(string.Empty); - - if (input is not null) - { - foreach (var item in input) - { - inputs[$"{item.Key}"] = $"{item.Value}"; - } - } - - return $"{inputs}"; - } - } -} diff --git a/Gateways/Tda/Libs/Extensions/Double.cs b/Gateways/Tda/Libs/Extensions/Double.cs deleted file mode 100644 index 2b9228625..000000000 --- a/Gateways/Tda/Libs/Extensions/Double.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace Terminal.Core.Extensions -{ - public static class DoubleExtensions - { - public static bool IsEqual(this double input, double num, double epsilon = double.Epsilon) - { - return Math.Abs(input - num) < epsilon; - } - } -} diff --git a/Gateways/Tda/Libs/Extensions/String.cs b/Gateways/Tda/Libs/Extensions/String.cs deleted file mode 100644 index 164b522ec..000000000 --- a/Gateways/Tda/Libs/Extensions/String.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System.Collections.Specialized; -using System; -using System.Text; -using System.Text.Json; -using System.Web; - -namespace Terminal.Core.Extensions -{ - public static class StringExtensions - { - public static int? ToInt(this string input) - { - return int.TryParse(input, out int o) ? o : null; - } - - public static double? ToDouble(this string input) - { - return double.TryParse(input, out double o) ? o : null; - } - - public static decimal? ToDecimal(this string input) - { - return decimal.TryParse(input, out decimal o) ? o : null; - } - - public static byte[] ToBytes(this string input) - { - return Encoding.UTF8.GetBytes(input); - } - - public static T Deserialize(this string input, JsonSerializerOptions options = null) - { - return JsonSerializer.Deserialize(input, options); - } - - public static NameValueCollection ToPairs(this string input) - { - return HttpUtility.ParseQueryString(new Uri(input).Query); - } - } -} diff --git a/Gateways/Tda/Libs/Models/ChainModels.cs b/Gateways/Tda/Libs/Models/ChainModels.cs deleted file mode 100644 index 578004fa6..000000000 --- a/Gateways/Tda/Libs/Models/ChainModels.cs +++ /dev/null @@ -1,282 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text.Json.Serialization; - -namespace Terminal.Connector.Tda.Models -{ - public struct Option - { - [JsonPropertyName("putCall")] - public string PutCall { get; set; } - - [JsonPropertyName("symbol")] - public string Symbol { get; set; } - - [JsonPropertyName("description")] - public string Description { get; set; } - - [JsonPropertyName("exchangeName")] - public string ExchangeName { get; set; } - - [JsonPropertyName("bid")] - public double? Bid { get; set; } - - [JsonPropertyName("ask")] - public double? Ask { get; set; } - - [JsonPropertyName("last")] - public double? Last { get; set; } - - [JsonPropertyName("mark")] - public double? Mark { get; set; } - - [JsonPropertyName("bidSize")] - public double? BidSize { get; set; } - - [JsonPropertyName("askSize")] - public double? AskSize { get; set; } - - [JsonPropertyName("bidAskSize")] - public string BidAskSize { get; set; } - - [JsonPropertyName("lastSize")] - public double? LastSize { get; set; } - - [JsonPropertyName("highPrice")] - public double? HighPrice { get; set; } - - [JsonPropertyName("lowPrice")] - public double? LowPrice { get; set; } - - [JsonPropertyName("openPrice")] - public double? OpenPrice { get; set; } - - [JsonPropertyName("closePrice")] - public double? ClosePrice { get; set; } - - [JsonPropertyName("totalVolume")] - public double? TotalVolume { get; set; } - - [JsonPropertyName("tradeDate")] - public DateTime TradeDate { get; set; } - - [JsonPropertyName("quoteTimeInLong")] - public long? QuoteTimeInLong { get; set; } - - [JsonPropertyName("tradeTimeInLong")] - public long? TradeTimeInLong { get; set; } - - [JsonPropertyName("netChange")] - public double? NetChange { get; set; } - - [JsonPropertyName("volatility")] - public double? Volatility { get; set; } - - [JsonPropertyName("delta")] - public double? Delta { get; set; } - - [JsonPropertyName("gamma")] - public double? Gamma { get; set; } - - [JsonPropertyName("theta")] - public double? Theta { get; set; } - - [JsonPropertyName("vega")] - public double? Vega { get; set; } - - [JsonPropertyName("rho")] - public double? Rho { get; set; } - - [JsonPropertyName("timeValue")] - public double? TimeValue { get; set; } - - [JsonPropertyName("openInterest")] - public double? OpenInterest { get; set; } - - [JsonPropertyName("inTheMoney")] - public bool? IsInTheMoney { get; set; } - - [JsonPropertyName("theoreticalOptionValue")] - public double? TheoreticalOptionValue { get; set; } - - [JsonPropertyName("theoreticalVolatility")] - public double? TheoreticalVolatility { get; set; } - - [JsonPropertyName("mini")] - public bool? IsMini { get; set; } - - [JsonPropertyName("nonStandard")] - public bool? IsNonStandard { get; set; } - - [JsonPropertyName("optionDeliverablesList")] - public List OptionDeliverablesList { get; set; } - - [JsonPropertyName("strikePrice")] - public double? StrikePrice { get; set; } - - [JsonPropertyName("lastTradingDay")] - public double? LastTradingDay { get; set; } - - [JsonPropertyName("expirationDate")] - public long? ExpirationDate { get; set; } - - [JsonPropertyName("expirationType")] - public string ExpirationType { get; set; } - - [JsonPropertyName("multiplier")] - public double? Multiplier { get; set; } - - [JsonPropertyName("intrinsicValue")] - public double? IntrinsicValue { get; set; } - - [JsonPropertyName("settlementType")] - public string SettlementType { get; set; } - - [JsonPropertyName("deliverableNote")] - public string DeliverableNote { get; set; } - - [JsonPropertyName("isIndexOption")] - public bool? IsIndexOption { get; set; } - - [JsonPropertyName("pennyPilot")] - public bool? IsPennyPilot { get; set; } - - [JsonPropertyName("percentChange")] - public double? PercentChange { get; set; } - - [JsonPropertyName("markChange")] - public double? MarkChange { get; set; } - - [JsonPropertyName("markPercentChange")] - public double? MarkPercentChange { get; set; } - } - - public struct OptionDeliverables - { - [JsonPropertyName("symbol")] - public string Symbol { get; set; } - - [JsonPropertyName("assetType")] - public string AssetType { get; set; } - - [JsonPropertyName("deliverableUnits")] - public string DeliverableUnits { get; set; } - - [JsonPropertyName("currencyType")] - public string CurrencyType { get; set; } - } - - public struct OptionChain - { - [JsonPropertyName("symbol")] - public string Symbol { get; set; } - - [JsonPropertyName("status")] - public string Status { get; set; } - - [JsonPropertyName("underlying")] - public Underlying? Underlying { get; set; } - - [JsonPropertyName("strategy")] - public string Strategy { get; set; } - - [JsonPropertyName("interval")] - public double? Interval { get; set; } - - [JsonPropertyName("isDelayed")] - public bool? IsDelayed { get; set; } - - [JsonPropertyName("isIndex")] - public bool? IsIndex { get; set; } - - [JsonPropertyName("daysToExpiration")] - public double? DaysToExpiration { get; set; } - - [JsonPropertyName("interestRate")] - public double? InterestRate { get; set; } - - [JsonPropertyName("underlyingPrice")] - public double? UnderlyingPrice { get; set; } - - [JsonPropertyName("volatility")] - public double? Volatility { get; set; } - - [JsonPropertyName("callExpDateMap")] - public Dictionary>> CallExpDateMap { get; set; } - - [JsonPropertyName("putExpDateMap")] - public Dictionary>> PutExpDateMap { get; set; } - } - - public struct Underlying - { - [JsonPropertyName("ask")] - public double? Ask { get; set; } - - [JsonPropertyName("askSize")] - public double? AskSize { get; set; } - - [JsonPropertyName("bid")] - public double? Bid { get; set; } - - [JsonPropertyName("bidSize")] - public double? BidSize { get; set; } - - [JsonPropertyName("change")] - public double? Change { get; set; } - - [JsonPropertyName("close")] - public double? Close { get; set; } - - [JsonPropertyName("delayed")] - public bool? Delayed { get; set; } - - [JsonPropertyName("description")] - public string Description { get; set; } - - [JsonPropertyName("exchangeName")] - public string ExchangeName { get; set; } - - [JsonPropertyName("fiftyTwoWeekHigh")] - public double? FiftyTwoWeekHigh { get; set; } - - [JsonPropertyName("fiftyTwoWeekLow")] - public double? FiftyTwoWeekLow { get; set; } - - [JsonPropertyName("highPrice")] - public double? HighPrice { get; set; } - - [JsonPropertyName("last")] - public double? Last { get; set; } - - [JsonPropertyName("lowPrice")] - public double? LowPrice { get; set; } - - [JsonPropertyName("mark")] - public double? Mark { get; set; } - - [JsonPropertyName("markChange")] - public double? MarkChange { get; set; } - - [JsonPropertyName("markPercentChange")] - public double? MarkPercentChange { get; set; } - - [JsonPropertyName("openPrice")] - public double? OpenPrice { get; set; } - - [JsonPropertyName("percentChange")] - public double? PercentChange { get; set; } - - [JsonPropertyName("quoteTime")] - public double? QuoteTime { get; set; } - - [JsonPropertyName("symbol")] - public string Symbol { get; set; } - - [JsonPropertyName("totalVolume")] - public double? TotalVolume { get; set; } - - [JsonPropertyName("tradeTime")] - public double? TradeTime { get; set; } - } -} diff --git a/Gateways/Tda/Libs/Models/UserModels.cs b/Gateways/Tda/Libs/Models/UserModels.cs deleted file mode 100644 index bde0e3e6a..000000000 --- a/Gateways/Tda/Libs/Models/UserModels.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using System.Text.Json.Serialization; - -namespace Terminal.Connector.Tda.Models -{ - public struct UserModel - { - [JsonPropertyName("redirect_url")] - public string RedirectUrl { get; set; } - - [JsonPropertyName("consumer_key")] - public string ConsumerKey { get; set; } - - [JsonPropertyName("security_code")] - public string SecurityCode { get; set; } - - [JsonPropertyName("access_token")] - public string AccessToken { get; set; } - - [JsonPropertyName("refresh_token")] - public string RefreshToken { get; set; } - - [JsonPropertyName("scope")] - public string Scope { get; set; } - - [JsonPropertyName("expires_in")] - public int ExpiresIn { get; set; } - - [JsonPropertyName("expiration_date")] - public long ExpirationDate { get; set; } - - [JsonPropertyName("refresh_token_expires_in")] - public int RefreshTokenExpiresIn { get; set; } - - [JsonPropertyName("token_type")] - public string TokenType { get; set; } - } -} diff --git a/Gateways/Tda/Libs/Tda.csproj b/Gateways/Tda/Libs/Tda.csproj deleted file mode 100644 index 10697500d..000000000 --- a/Gateways/Tda/Libs/Tda.csproj +++ /dev/null @@ -1,25 +0,0 @@ - - - - net7.0 - True - 1.0.8-prerelease - Internal package for trading and analysis - artemiusgreat - indemos.com - http://indemos.com - https://github.com/Indemos/Terminal-Connector-Simulation - finance trading market stock backtester algorithmic-trading - MIT - - - - - - - - - - - - diff --git a/Gateways/Tda/Libs/Tracer.cs b/Gateways/Tda/Libs/Tracer.cs deleted file mode 100644 index d12b33335..000000000 --- a/Gateways/Tda/Libs/Tracer.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Newtonsoft.Json.Serialization; -using System; -using System.Diagnostics; - -namespace Terminal.Connector.Tda -{ - public class ConsoleTraceWriter : ITraceWriter - { - public TraceLevel LevelFilter => TraceLevel.Verbose; - - public void Trace(TraceLevel state, string message, Exception e) - { - Console.WriteLine($"{state}: {message} Exception: {e?.Message}"); - } - } -} diff --git a/Gateways/Tda/README.md b/Gateways/Tda/README.md deleted file mode 100644 index 9c5237538..000000000 --- a/Gateways/Tda/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# Terminal Connector - Simulation - -Internal cross-platform library for trading and analysis. -Reads historical asset prices from file and allows to backtest order execution based on some trading signals. -Has references from other apps in [this list](https://github.com/Indemos). The sample is [here](https://github.com/Indemos/Terminal/blob/main/Terminal.Client/Pages/Pairs.razor.cs). - -# Status - -``` -Install-Package Terminal.Simulation -``` - -![GitHub Workflow Status (with event)](https://img.shields.io/github/actions/workflow/status/Indemos/Terminal-Connector-Simulation/dotnet.yml?event=push) -![GitHub](https://img.shields.io/github/license/Indemos/Terminal-Connector-Simulation) -![GitHub](https://img.shields.io/badge/system-Windows%20%7C%20Linux%20%7C%20Mac-blue) diff --git a/Gateways/Tda/Tests/Terminal.Tests.csproj b/Gateways/Tda/Tests/Terminal.Tests.csproj deleted file mode 100644 index bfa429ed7..000000000 --- a/Gateways/Tda/Tests/Terminal.Tests.csproj +++ /dev/null @@ -1,28 +0,0 @@ - - - - net7.0 - disable - disable - false - - - - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - - - - - diff --git a/Gateways/Tda/Tests/Usings.cs b/Gateways/Tda/Tests/Usings.cs deleted file mode 100644 index 8c927eb74..000000000 --- a/Gateways/Tda/Tests/Usings.cs +++ /dev/null @@ -1 +0,0 @@ -global using Xunit; \ No newline at end of file diff --git a/Solution.sln b/Solution.sln index 4cd003d3a..e8c9afb0a 100644 --- a/Solution.sln +++ b/Solution.sln @@ -9,17 +9,21 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Gateways", "Gateways", "{A7 EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Core", "Core\Core.csproj", "{97D6A4C0-1682-4C0D-A449-D8BB9C606594}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tda", "Tda", "{9AF565D1-31F6-416A-9A71-C755E355A741}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Ameritrade", "Ameritrade", "{9AF565D1-31F6-416A-9A71-C755E355A741}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Simulation", "Simulation", "{CAD78AFC-793C-45A9-B5A5-628011D599E4}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Simulation", "Gateways\Simulation\Libs\Simulation.csproj", "{1F838D97-6CF0-40E4-813F-B4C4707E4DDC}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Terminal.Tests", "Gateways\Simulation\Tests\Terminal.Tests.csproj", "{8F9D118E-9A86-4D99-8F94-126BB0DEEC12}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ameritrade", "Gateways\Ameritrade\Libs\Ameritrade.csproj", "{697A2F71-6DC7-4AA3-9D5B-61899605BE3F}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tda", "Gateways\Tda\Libs\Tda.csproj", "{697A2F71-6DC7-4AA3-9D5B-61899605BE3F}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests", "Gateways\Ameritrade\Tests\Tests.csproj", "{8DA38824-8E1A-4E42-92AF-D1AA6F4AA730}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Terminal.Tests", "Gateways\Tda\Tests\Terminal.Tests.csproj", "{9BC0EE91-9B21-4722-A7C4-36D966E6F61D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests", "Gateways\Simulation\Tests\Tests.csproj", "{7FFC95C5-48CA-4475-BEA3-740CB427345C}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Alpaca", "Alpaca", "{E7CD6538-C7F9-4D33-9F0A-23A699FC84FB}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Alpaca", "Gateways\Alpaca\Libs\Alpaca.csproj", "{9DF6CCD0-280E-4387-8222-460D52B17EB2}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -53,14 +57,6 @@ Global {1F838D97-6CF0-40E4-813F-B4C4707E4DDC}.Release|Any CPU.Build.0 = Release|Any CPU {1F838D97-6CF0-40E4-813F-B4C4707E4DDC}.Release|x64.ActiveCfg = Release|Any CPU {1F838D97-6CF0-40E4-813F-B4C4707E4DDC}.Release|x64.Build.0 = Release|Any CPU - {8F9D118E-9A86-4D99-8F94-126BB0DEEC12}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8F9D118E-9A86-4D99-8F94-126BB0DEEC12}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8F9D118E-9A86-4D99-8F94-126BB0DEEC12}.Debug|x64.ActiveCfg = Debug|Any CPU - {8F9D118E-9A86-4D99-8F94-126BB0DEEC12}.Debug|x64.Build.0 = Debug|Any CPU - {8F9D118E-9A86-4D99-8F94-126BB0DEEC12}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8F9D118E-9A86-4D99-8F94-126BB0DEEC12}.Release|Any CPU.Build.0 = Release|Any CPU - {8F9D118E-9A86-4D99-8F94-126BB0DEEC12}.Release|x64.ActiveCfg = Release|Any CPU - {8F9D118E-9A86-4D99-8F94-126BB0DEEC12}.Release|x64.Build.0 = Release|Any CPU {697A2F71-6DC7-4AA3-9D5B-61899605BE3F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {697A2F71-6DC7-4AA3-9D5B-61899605BE3F}.Debug|Any CPU.Build.0 = Debug|Any CPU {697A2F71-6DC7-4AA3-9D5B-61899605BE3F}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -69,14 +65,30 @@ Global {697A2F71-6DC7-4AA3-9D5B-61899605BE3F}.Release|Any CPU.Build.0 = Release|Any CPU {697A2F71-6DC7-4AA3-9D5B-61899605BE3F}.Release|x64.ActiveCfg = Release|Any CPU {697A2F71-6DC7-4AA3-9D5B-61899605BE3F}.Release|x64.Build.0 = Release|Any CPU - {9BC0EE91-9B21-4722-A7C4-36D966E6F61D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9BC0EE91-9B21-4722-A7C4-36D966E6F61D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9BC0EE91-9B21-4722-A7C4-36D966E6F61D}.Debug|x64.ActiveCfg = Debug|Any CPU - {9BC0EE91-9B21-4722-A7C4-36D966E6F61D}.Debug|x64.Build.0 = Debug|Any CPU - {9BC0EE91-9B21-4722-A7C4-36D966E6F61D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9BC0EE91-9B21-4722-A7C4-36D966E6F61D}.Release|Any CPU.Build.0 = Release|Any CPU - {9BC0EE91-9B21-4722-A7C4-36D966E6F61D}.Release|x64.ActiveCfg = Release|Any CPU - {9BC0EE91-9B21-4722-A7C4-36D966E6F61D}.Release|x64.Build.0 = Release|Any CPU + {8DA38824-8E1A-4E42-92AF-D1AA6F4AA730}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8DA38824-8E1A-4E42-92AF-D1AA6F4AA730}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8DA38824-8E1A-4E42-92AF-D1AA6F4AA730}.Debug|x64.ActiveCfg = Debug|Any CPU + {8DA38824-8E1A-4E42-92AF-D1AA6F4AA730}.Debug|x64.Build.0 = Debug|Any CPU + {8DA38824-8E1A-4E42-92AF-D1AA6F4AA730}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8DA38824-8E1A-4E42-92AF-D1AA6F4AA730}.Release|Any CPU.Build.0 = Release|Any CPU + {8DA38824-8E1A-4E42-92AF-D1AA6F4AA730}.Release|x64.ActiveCfg = Release|Any CPU + {8DA38824-8E1A-4E42-92AF-D1AA6F4AA730}.Release|x64.Build.0 = Release|Any CPU + {7FFC95C5-48CA-4475-BEA3-740CB427345C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7FFC95C5-48CA-4475-BEA3-740CB427345C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7FFC95C5-48CA-4475-BEA3-740CB427345C}.Debug|x64.ActiveCfg = Debug|Any CPU + {7FFC95C5-48CA-4475-BEA3-740CB427345C}.Debug|x64.Build.0 = Debug|Any CPU + {7FFC95C5-48CA-4475-BEA3-740CB427345C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7FFC95C5-48CA-4475-BEA3-740CB427345C}.Release|Any CPU.Build.0 = Release|Any CPU + {7FFC95C5-48CA-4475-BEA3-740CB427345C}.Release|x64.ActiveCfg = Release|Any CPU + {7FFC95C5-48CA-4475-BEA3-740CB427345C}.Release|x64.Build.0 = Release|Any CPU + {9DF6CCD0-280E-4387-8222-460D52B17EB2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9DF6CCD0-280E-4387-8222-460D52B17EB2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9DF6CCD0-280E-4387-8222-460D52B17EB2}.Debug|x64.ActiveCfg = Debug|Any CPU + {9DF6CCD0-280E-4387-8222-460D52B17EB2}.Debug|x64.Build.0 = Debug|Any CPU + {9DF6CCD0-280E-4387-8222-460D52B17EB2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9DF6CCD0-280E-4387-8222-460D52B17EB2}.Release|Any CPU.Build.0 = Release|Any CPU + {9DF6CCD0-280E-4387-8222-460D52B17EB2}.Release|x64.ActiveCfg = Release|Any CPU + {9DF6CCD0-280E-4387-8222-460D52B17EB2}.Release|x64.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -85,9 +97,11 @@ Global {9AF565D1-31F6-416A-9A71-C755E355A741} = {A7B0DEF1-75C8-440A-ADDC-D6C1B758D2AA} {CAD78AFC-793C-45A9-B5A5-628011D599E4} = {A7B0DEF1-75C8-440A-ADDC-D6C1B758D2AA} {1F838D97-6CF0-40E4-813F-B4C4707E4DDC} = {CAD78AFC-793C-45A9-B5A5-628011D599E4} - {8F9D118E-9A86-4D99-8F94-126BB0DEEC12} = {CAD78AFC-793C-45A9-B5A5-628011D599E4} {697A2F71-6DC7-4AA3-9D5B-61899605BE3F} = {9AF565D1-31F6-416A-9A71-C755E355A741} - {9BC0EE91-9B21-4722-A7C4-36D966E6F61D} = {9AF565D1-31F6-416A-9A71-C755E355A741} + {8DA38824-8E1A-4E42-92AF-D1AA6F4AA730} = {9AF565D1-31F6-416A-9A71-C755E355A741} + {7FFC95C5-48CA-4475-BEA3-740CB427345C} = {CAD78AFC-793C-45A9-B5A5-628011D599E4} + {E7CD6538-C7F9-4D33-9F0A-23A699FC84FB} = {A7B0DEF1-75C8-440A-ADDC-D6C1B758D2AA} + {9DF6CCD0-280E-4387-8222-460D52B17EB2} = {E7CD6538-C7F9-4D33-9F0A-23A699FC84FB} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {DF743EB8-FAC2-4496-8A95-66A93CF19B8B}