Skip to content

Commit

Permalink
Gateways
Browse files Browse the repository at this point in the history
  • Loading branch information
artemiusgreat committed Nov 25, 2024
1 parent 620e7a1 commit 89ae075
Show file tree
Hide file tree
Showing 12 changed files with 102 additions and 118 deletions.
3 changes: 2 additions & 1 deletion Core/Models/Transactions/OrderModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public class OrderModel : ICloneable
/// <summary>
/// Id
/// </summary>
public virtual string Id => Transaction?.Id;
public virtual string Id { get; set; }

/// <summary>
/// Name
Expand Down Expand Up @@ -84,6 +84,7 @@ public OrderModel()
{
Orders = [];
OrderStream = o => { };
Id = $"{Guid.NewGuid():N}".ToUpper();
Descriptor = $"{Guid.NewGuid():N}".ToUpper();
}

Expand Down
6 changes: 1 addition & 5 deletions Core/Models/Transactions/TransactionModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,7 @@ public class TransactionModel : ICloneable
/// <summary>
/// Constructor
/// </summary>
public TransactionModel()
{
Id = $"{Guid.NewGuid():N}".ToUpper();
Time = DateTime.Now;
}
public TransactionModel() => Time = DateTime.Now;

/// <summary>
/// Clone
Expand Down
1 change: 0 additions & 1 deletion Core/Validators/OrderValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ public OrderValidator()

When(o => o.Transaction is not null, () =>
{
RuleFor(o => o.Id).NotEmpty();
RuleFor(o => o.Side).NotEmpty();
RuleFor(o => o.Transaction.Status).Empty();
RuleFor(o => o.Transaction.CurrentVolume).Empty();
Expand Down
9 changes: 5 additions & 4 deletions Gateway/Alpaca/Libs/Adapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
using Alpaca.Markets;
using Distribution.Services;
using Distribution.Stream;
using Microsoft.AspNetCore.Identity.Data;
using System;
using System.Collections;
using System.Collections.Generic;
Expand Down Expand Up @@ -449,21 +448,22 @@ public override async Task<ResponseModel<IList<PointModel>>> GetPoints(PointScre
/// <param name="orders"></param>
/// <returns></returns>
public override async Task<ResponseModel<IList<OrderModel>>> CreateOrders(params OrderModel[] orders)

{
var response = new ResponseModel<IList<OrderModel>> { Data = [] };

foreach (var order in orders)
{
try
{
Account.Orders[order.Id] = order;

var exOrder = ExternalMap.GetOrder(order);
var exResponse = await _tradingClient.PostOrderAsync(exOrder);

order.Transaction.Id = $"{exResponse.OrderId}";
order.Transaction.Status = InternalMap.GetStatus(exResponse.OrderStatus);

Account.Orders[order.Id] = order;

response.Data.Add(order);
}
catch (Exception e)
Expand All @@ -486,7 +486,7 @@ public override async Task<ResponseModel<IList<OrderModel>>> DeleteOrders(params

foreach (var order in orders)
{
await _tradingClient.CancelOrderAsync(Guid.Parse(order.Id));
await _tradingClient.CancelOrderAsync(Guid.Parse(order.Transaction.Id));
}

return response;
Expand All @@ -506,6 +506,7 @@ protected virtual void OnPoint(IQuote streamPoint)
var point = InternalMap.GetPrice(streamPoint, instrument);

instrument.Name = streamPoint.Symbol;
instrument.Point = point;
instrument.Points.Add(point);
instrument.PointGroups.Add(point, instrument.TimeFrame);

Expand Down
85 changes: 29 additions & 56 deletions Gateway/Alpaca/Libs/Maps/ExternalMap.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using Alpaca.Markets;
using System.Drawing;
using System.Linq;
using System.Xml.Linq;
using Terminal.Core.Enums;
using Terminal.Core.Models;

Expand All @@ -12,74 +14,28 @@ public class ExternalMap
/// </summary>
/// <param name="orders"></param>
/// <returns></returns>
public static OrderBase GetOrder(OrderModel order)
public static NewOrderRequest GetOrder(OrderModel order)
{
var instrument = order.Transaction.Instrument;
var name = instrument.Name;
var volume = OrderQuantity.Fractional((decimal)order.Transaction.Volume);
var side = order.Side is OrderSideEnum.Buy ? OrderSide.Buy : OrderSide.Sell;

SimpleOrderBase getOrder()
{
switch (order.Type)
{
case OrderTypeEnum.Stop: return side.Stop(name, volume, (decimal)order.Price);
case OrderTypeEnum.Limit: return side.Limit(name, volume, (decimal)order.Price);
case OrderTypeEnum.StopLimit: return side.StopLimit(name, volume, (decimal)order.ActivationPrice, (decimal)order.Price);
}

return side.Market(name, volume);
}

var exOrder = getOrder();

exOrder.Duration = GetTimeInForce(order.TimeSpan);

var exBrace = exOrder as OrderBase;
var orderType = GetOrderType(order.Type);
var duration = GetTimeInForce(order.TimeSpan);
var exOrder = new NewOrderRequest(name, volume, side, orderType, duration);
var braces = order.Orders.Where(o => o.Instruction is InstructionEnum.Brace);

if (braces.Any())
{
switch (order.Side)
{
case OrderSideEnum.Buy:
{
var TP = GetBracePrice(order, 1);
var SL = GetBracePrice(order, -1);
exBrace = GetBraces(exOrder, SL, TP);
}
break;
var TP = GetBracePrice(order, order.Side is OrderSideEnum.Buy ? 1 : -1);
var SL = GetBracePrice(order, order.Side is OrderSideEnum.Buy ? -1 : 1);

case OrderSideEnum.Sell:
{
var SL = GetBracePrice(order, 1);
var TP = GetBracePrice(order, -1);
exBrace = GetBraces(exOrder, SL, TP);
}
break;
}
}

return exBrace;
}

/// <summary>
/// Convert child orders to brackets
/// </summary>
/// <param name="order"></param>
/// <param name="SL"></param>
/// <param name="TP"></param>
/// <returns></returns>
protected static OrderBase GetBraces(SimpleOrderBase order, double? SL, double? TP)
{
switch (true)
{
case true when SL is not null && TP is not null: return order.StopLoss((decimal)SL).TakeProfit((decimal)TP);
case true when TP is not null: return order.TakeProfit((decimal)TP);
case true when SL is not null: return order.StopLoss((decimal)SL);
exOrder.OrderClass = OrderClass.Bracket;
exOrder.StopLossStopPrice = SL is null ? null : (decimal)SL;
exOrder.TakeProfitLimitPrice = TP is null ? null : (decimal)TP;
}

return order;
return exOrder;
}

/// <summary>
Expand Down Expand Up @@ -116,5 +72,22 @@ public static TimeInForce GetTimeInForce(OrderTimeSpanEnum? timeSpan)

return TimeInForce.Gtc;
}

/// <summary>
/// Get order type
/// </summary>
/// <param name="orderType"></param>
/// <returns></returns>
public static OrderType GetOrderType(OrderTypeEnum? orderType)
{
switch (orderType)
{
case OrderTypeEnum.Stop: return OrderType.Stop;
case OrderTypeEnum.Limit: return OrderType.Limit;
case OrderTypeEnum.StopLimit: return OrderType.StopLimit;
}

return OrderType.Market;
}
}
}
11 changes: 6 additions & 5 deletions Gateway/Schwab/Libs/Adapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ public override async Task<ResponseModel<IList<PointModel>>> GetPoints(PointScre
/// <returns></returns>
public override async Task<ResponseModel<IList<OrderModel>>> CreateOrders(params OrderModel[] orders)
{
var response = new ResponseModel<IList<OrderModel>>();
var response = new ResponseModel<IList<OrderModel>> { Data = [] };

foreach (var order in orders)
{
Expand All @@ -355,7 +355,7 @@ public override async Task<ResponseModel<IList<OrderModel>>> CreateOrders(params
/// <returns></returns>
public override async Task<ResponseModel<IList<OrderModel>>> DeleteOrders(params OrderModel[] orders)
{
var response = new ResponseModel<IList<OrderModel>>();
var response = new ResponseModel<IList<OrderModel>> { Data = [] };

foreach (var order in orders)
{
Expand Down Expand Up @@ -597,6 +597,7 @@ protected virtual void OnPoint(IEnumerable<StreamDataMessage> streamPoints)
point.Last = double.TryParse($"{data.Get(map.Get("Last Price"))}", out var x5) ? x5 : (point.Bid ?? point.Ask);

instrument.Name = instrumentName;
instrument.Point = point;
instrument.Points.Add(point);
instrument.PointGroups.Add(point, instrument.TimeFrame);

Expand Down Expand Up @@ -712,6 +713,8 @@ protected virtual async Task<ResponseModel<OrderModel>> CreateOrder(OrderModel o

try
{
Account.Orders[order.Id] = order;

var exOrder = ExternalMap.GetOrder(order);
var exResponse = await SendData<OrderMessage>($"/trader/v1/accounts/{_accountCode}/orders", HttpMethod.Post, exOrder);

Expand All @@ -730,8 +733,6 @@ protected virtual async Task<ResponseModel<OrderModel>> CreateOrder(OrderModel o

inResponse.Data.Transaction.Id = orderId;
inResponse.Data.Transaction.Status = OrderStatusEnum.Filled;

Account.Orders[order.Id] = order;
}
}
catch (Exception e)
Expand All @@ -753,7 +754,7 @@ protected virtual async Task<ResponseModel<OrderModel>> DeleteOrder(OrderModel o

try
{
var exResponse = await SendData<OrderMessage>($"/trader/v1/accounts/{_accountCode}/orders/{order.Id}", HttpMethod.Delete);
var exResponse = await SendData<OrderMessage>($"/trader/v1/accounts/{_accountCode}/orders/{order.Transaction.Id}", HttpMethod.Delete);

if ((int)exResponse.Message.StatusCode >= 400)
{
Expand Down
4 changes: 2 additions & 2 deletions Gateway/Schwab/Libs/Maps/ExternalMap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,8 @@ public static string GetInstrumentType(InstrumentEnum? message)
{
switch (message)
{
case InstrumentEnum.Shares: return "Equity";
case InstrumentEnum.Options: return "Option";
case InstrumentEnum.Shares: return "EQUITY";
case InstrumentEnum.Options: return "OPTION";
}

return null;
Expand Down
4 changes: 3 additions & 1 deletion Gateway/Schwab/SignIn/Controllers/HomeController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,10 @@ public async Task<ActionResult<string>> Get(string code)

[HttpGet]
[Route("signin")]
public ActionResult SignIn(string clientId, string clientSecret)
public ActionResult SignIn()
{
var clientId = $"{Configuration["Schwab:ClientId"]}";
var clientSecret = $"{Configuration["Schwab:ClientSecret"]}";
var query = HttpUtility.ParseQueryString(string.Empty);

query.Add("response_type", "code");
Expand Down
37 changes: 22 additions & 15 deletions Gateway/Simulation/Libs/Adapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -187,12 +187,13 @@ public override Task<ResponseModel<StatusEnum>> Unsubscribe(InstrumentModel inst
/// <param name="orders"></param>
public override Task<ResponseModel<IList<OrderModel>>> CreateOrders(params OrderModel[] orders)
{
var response = new ResponseModel<IList<OrderModel>>();
var response = new ResponseModel<IList<OrderModel>> { Data = [] };
var validator = InstanceService<OrderPriceValidator>.Instance;

response.Errors = orders
.SelectMany(o => validator.Validate(o).Errors.Select(error => new ErrorModel { ErrorMessage = error.ErrorMessage }))
.ToList();
response.Errors = [.. orders.SelectMany(o => validator
.Validate(o)
.Errors
.Select(error => new ErrorModel { ErrorMessage = error.ErrorMessage }))];

if (response.Errors.Count > 0)
{
Expand All @@ -212,6 +213,8 @@ public override Task<ResponseModel<IList<OrderModel>>> CreateOrders(params Order
case OrderTypeEnum.StopLimit: SendPendingOrder(nextOrder); break;
case OrderTypeEnum.Market: SendOrder(nextOrder); break;
}

response.Data.Add(nextOrder);
}
}

Expand Down Expand Up @@ -249,7 +252,9 @@ protected virtual OrderModel SendPendingOrder(OrderModel order)
{
var nextOrder = order.Clone() as OrderModel;

nextOrder.Transaction.Id = order.Id;
nextOrder.Transaction.Status = OrderStatusEnum.Pending;

Account.Orders[nextOrder.Id] = nextOrder;

return order;
Expand All @@ -262,29 +267,29 @@ protected virtual OrderModel SendPendingOrder(OrderModel order)
/// <returns></returns>
protected virtual OrderModel SendOrder(OrderModel order)
{
order.Transaction.Id = order.Id;

if (Account.Positions.TryGetValue(order.Name, out var currentOrder))
{
var (inOrder, outOrder) = Equals(currentOrder.Side, order.Side) ?
var (nextOrder, previousOrder) = Equals(currentOrder.Side, order.Side) ?
IncreaseSide(currentOrder, order) :
DecreaseSide(currentOrder, order);

Account.Positions.Remove(order.Name, out var item);

if (inOrder?.Transaction?.Volume.Is(0) is false)
if (previousOrder?.Transaction?.Volume.Is(0) is false)
{
Account.Positions[order.Name] = inOrder;
Account.Deals.Add(previousOrder);
Account.Balance += previousOrder.GetGainEstimate();
}

if (outOrder?.Transaction?.Volume.Is(0) is false)
{
Account.Deals.Add(outOrder);
Account.Balance += outOrder.GetGainEstimate();
}

return order;
order = nextOrder;
}

Account.Positions[order.Name] = order;
if (order?.Transaction?.Volume.Is(0) is false)
{
Account.Positions[order.Name] = order;
}

return order;
}
Expand Down Expand Up @@ -359,6 +364,7 @@ protected virtual (OrderModel, OrderModel) IncreaseSide(OrderModel order, OrderM
var nextOrder = order.Clone() as OrderModel;
var volume = nextOrder.Transaction.CurrentVolume + update.Transaction.Volume;

nextOrder.Transaction.Id = update.Id;
nextOrder.Transaction.Volume = volume;
nextOrder.Transaction.CurrentVolume = volume;
nextOrder.Transaction.Time = update.Transaction.Time;
Expand All @@ -383,6 +389,7 @@ protected virtual (OrderModel, OrderModel) DecreaseSide(OrderModel order, OrderM
var previousVolume = nextOrder.Transaction.CurrentVolume ?? 0;
var nextVolume = Math.Abs(previousVolume - updateVolume);

nextOrder.Transaction.Id = update.Id;
nextOrder.Transaction.Volume = nextVolume;
nextOrder.Transaction.CurrentVolume = nextVolume;
nextOrder.Transaction.Time = update.Transaction.Time;
Expand Down
4 changes: 2 additions & 2 deletions Gateway/Simulation/Tests/Positions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public void CreatePendingOrder(
Assert.Equal(outOrder.Price, openPrice);
Assert.Equal(outOrder.TimeSpan, OrderTimeSpanEnum.Gtc);
Assert.NotEmpty(outOrder.Transaction.Id);
Assert.Equal(outOrder.Transaction.Id, order.Transaction.Id);
Assert.Equal(outOrder.Transaction.Id, order.Id);
Assert.Equal(outOrder.Transaction.Status, OrderStatusEnum.Pending);
}

Expand Down Expand Up @@ -126,7 +126,7 @@ public void CreateMarketOrder(
Assert.Equal(position.Type, OrderTypeEnum.Market);
Assert.Equal(position.TimeSpan, OrderTimeSpanEnum.Gtc);
Assert.Equal(position.Transaction.Status, OrderStatusEnum.Filled);
Assert.Equal(position.Transaction.Id, order.Transaction.Id);
Assert.Equal(position.Transaction.Id, order.Id);
Assert.Equal(position.Transaction.Volume, order.Transaction.Volume);
Assert.Equal(position.Transaction.CurrentVolume, order.Transaction.Volume);
Assert.NotEmpty(position.Transaction.Id);
Expand Down
Loading

0 comments on commit 89ae075

Please sign in to comment.