Skip to content

Commit

Permalink
Update DefaultBrokerageModel
Browse files Browse the repository at this point in the history
  • Loading branch information
wtindall1 committed Sep 23, 2024
1 parent 9a4ac7b commit 2f9290b
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 8 deletions.
7 changes: 6 additions & 1 deletion Common/Brokerages/DefaultBrokerageModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,12 @@ public virtual void ApplySplit(List<OrderTicket> tickets, Split split)
LimitPrice = ticket.OrderType.IsLimitOrder() ? ticket.Get(OrderField.LimitPrice)*splitFactor : (decimal?) null,
StopPrice = ticket.OrderType.IsStopOrder() ? ticket.Get(OrderField.StopPrice)*splitFactor : (decimal?) null,
TriggerPrice = ticket.OrderType == OrderType.LimitIfTouched ? ticket.Get(OrderField.TriggerPrice) * splitFactor : (decimal?) null,
TrailingAmount = ticket.OrderType == OrderType.TrailingStop && !ticket.Get<bool>(OrderField.TrailingAsPercentage) ? ticket.Get(OrderField.TrailingAmount) * splitFactor : (decimal?) null
TrailingAmount =
(ticket.OrderType == OrderType.TrailingStop || ticket.OrderType == OrderType.TrailingStopLimit)
&& !ticket.Get<bool>(OrderField.TrailingAsPercentage)
? ticket.Get(OrderField.TrailingAmount) * splitFactor
: (decimal?) null,
LimitOffset = ticket.OrderType == OrderType.TrailingStopLimit ? ticket.Get(OrderField.LimitOffset) * splitFactor : (decimal?) null,
}));
}

Expand Down
8 changes: 6 additions & 2 deletions Common/Orders/OrderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ public static bool IsLimitOrder(this OrderType orderType)
{
return orderType == OrderType.Limit
|| orderType == OrderType.StopLimit
|| orderType == OrderType.LimitIfTouched;
|| orderType == OrderType.LimitIfTouched
|| orderType == OrderType.TrailingStopLimit;
}

/// <summary>
Expand All @@ -74,7 +75,10 @@ public static bool IsLimitOrder(this OrderType orderType)
/// <returns>True if the order is a stop order, false otherwise</returns>
public static bool IsStopOrder(this OrderType orderType)
{
return orderType == OrderType.StopMarket || orderType == OrderType.StopLimit || orderType == OrderType.TrailingStop;
return orderType == OrderType.StopMarket
|| orderType == OrderType.StopLimit
|| orderType == OrderType.TrailingStop
|| orderType == OrderType.TrailingStopLimit;
}
}
}
7 changes: 6 additions & 1 deletion Common/Orders/OrderField.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ public enum OrderField
/// <summary>
/// Whether the trailing amount for a <see cref="TrailingStopOrder"/> is a percentage or an absolute currency value (4)
/// </summary>
TrailingAsPercentage
TrailingAsPercentage,

/// <summary>
/// The limit offset amount for a <see cref="TrailingStopLimitOrder"/> (5)
/// </summary>
LimitOffset
}
}
30 changes: 28 additions & 2 deletions Common/Orders/OrderTicket.cs
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,10 @@ public T Get<T>(OrderField field)
{
fieldValue = AccessOrder<LimitIfTouchedOrder, decimal>(this, field, o => o.LimitPrice, r => r.LimitPrice);
}
else if (_submitRequest.OrderType == OrderType.TrailingStopLimit)
{
fieldValue = AccessOrder<TrailingStopLimitOrder, decimal>(this, field, o => o.LimitPrice, r => r.LimitPrice);
}
break;

case OrderField.StopPrice:
Expand All @@ -284,18 +288,40 @@ public T Get<T>(OrderField field)
{
fieldValue = AccessOrder<TrailingStopOrder, decimal>(this, field, o => o.StopPrice, r => r.StopPrice);
}
else if (_submitRequest.OrderType == OrderType.TrailingStopLimit)
{
fieldValue = AccessOrder<TrailingStopLimitOrder, decimal>(this, field, o => o.StopPrice, r => r.StopPrice);
}
break;

case OrderField.TriggerPrice:
fieldValue = AccessOrder<LimitIfTouchedOrder, decimal>(this, field, o => o.TriggerPrice, r => r.TriggerPrice);
break;

case OrderField.TrailingAmount:
fieldValue = AccessOrder<TrailingStopOrder, decimal>(this, field, o => o.TrailingAmount, r => r.TrailingAmount);
if (_submitRequest.OrderType == OrderType.TrailingStop)
{
fieldValue = AccessOrder<TrailingStopOrder, decimal>(this, field, o => o.TrailingAmount, r => r.TrailingAmount);
}
else if (_submitRequest.OrderType == OrderType.TrailingStopLimit)
{
fieldValue = AccessOrder<TrailingStopLimitOrder, decimal>(this, field, o => o.TrailingAmount, r => r.TrailingAmount);
}
break;

case OrderField.TrailingAsPercentage:
fieldValue = AccessOrder<TrailingStopOrder, bool>(this, field, o => o.TrailingAsPercentage, r => r.TrailingAsPercentage);
if (_submitRequest.OrderType == OrderType.TrailingStop)
{
fieldValue = AccessOrder<TrailingStopOrder, bool>(this, field, o => o.TrailingAsPercentage, r => r.TrailingAsPercentage);
}
else if (_submitRequest.OrderType == OrderType.TrailingStopLimit)
{
fieldValue = AccessOrder<TrailingStopLimitOrder, bool>(this, field, o => o.TrailingAsPercentage, r => r.TrailingAsPercentage);
}
break;

case OrderField.LimitOffset:
fieldValue = AccessOrder<TrailingStopLimitOrder, decimal>(this, field, o => o.LimitOffset, r => r.LimitOffset);
break;

default:
Expand Down
47 changes: 45 additions & 2 deletions Tests/Common/Brokerages/DefaultBrokerageModelTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ public void ApplySplitWorksAsExpected()
OrderType.Limit,
OrderType.StopLimit,
OrderType.LimitIfTouched,
OrderType.TrailingStop
OrderType.TrailingStop,
OrderType.TrailingStopLimit
};

var algorithm = new BrokerageTransactionHandlerTests.TestAlgorithm
Expand Down Expand Up @@ -122,6 +123,10 @@ public void ApplySplitWorksAsExpected()
orderRequest = new SubmitOrderRequest(OrderType.TrailingStop, SecurityType.Equity, Symbols.IBM, 100, stopPrice: 10, 0, 0,
trailingAmount: 0.5m, trailingAsPercentage: false, DateTime.UtcNow, "");
break;
case OrderType.TrailingStopLimit:
orderRequest = new SubmitOrderRequest(OrderType.TrailingStopLimit, SecurityType.Equity, Symbols.IBM, 100, stopPrice: 10,
limitPrice: 11, 0, trailingAmount: 2, trailingAsPercentage: false, limitOffset: 1, DateTime.UtcNow, "");
break;
}
algorithm.Transactions.AddOrder(orderRequest);
var ticket = new OrderTicket(algorithm.Transactions, orderRequest);
Expand Down Expand Up @@ -151,11 +156,16 @@ public void ApplySplitWorksAsExpected()
Assert.AreEqual(5, order.GetPropertyValue("StopPrice"));
Assert.AreEqual(0.25m, order.GetPropertyValue("TrailingAmount"));
break;
case OrderType.TrailingStopLimit:
Assert.AreEqual(5, order.GetPropertyValue("StopPrice"));
Assert.AreEqual(1, order.GetPropertyValue("TrailingAmount"));
Assert.AreEqual(5.5m, order.GetPropertyValue("LimitPrice"));
Assert.AreEqual(0.5m, order.GetPropertyValue("LimitOffset"));
break;
}
}
}


[Test]
public void AppliesSplitOnlyWhenTrailingStopOrderTrailingAmountIsNotPercentage([Values] bool trailingAsPercentage)
{
Expand All @@ -172,6 +182,7 @@ public void AppliesSplitOnlyWhenTrailingStopOrderTrailingAmountIsNotPercentage([

var tickets = new List<OrderTicket>();
var orderTime = new DateTime(2023, 07, 21, 12, 0, 0);

var orderRequest = new SubmitOrderRequest(OrderType.TrailingStop, SecurityType.Equity, Symbols.IBM, 100, stopPrice: 10, 0, 0,
trailingAmount: 0.1m, trailingAsPercentage, orderTime, "");
algorithm.Transactions.AddOrder(orderRequest);
Expand All @@ -188,6 +199,38 @@ public void AppliesSplitOnlyWhenTrailingStopOrderTrailingAmountIsNotPercentage([
Assert.AreEqual(trailingAsPercentage ? 0.1m : 0.05m, order.GetPropertyValue("TrailingAmount"));
}

[Test]
public void AppliesSplitOnlyWhenTrailingStopLimitOrderTrailingAmountIsNotPercentage([Values] bool trailingAsPercentage)
{
var algorithm = new BrokerageTransactionHandlerTests.TestAlgorithm
{
HistoryProvider = new BrokerageTransactionHandlerTests.EmptyHistoryProvider()
};
var transactionHandler = new BacktestingTransactionHandler();
using var backtestingBrokerage = new BacktestingBrokerage(algorithm);
transactionHandler.Initialize(algorithm, backtestingBrokerage, new TestResultHandler(Console.WriteLine));

algorithm.Transactions.SetOrderProcessor(transactionHandler);
algorithm.AddEquity("IBM");

var tickets = new List<OrderTicket>();
var orderTime = new DateTime(2023, 07, 21, 12, 0, 0);

var orderRequest = new SubmitOrderRequest(OrderType.TrailingStopLimit, SecurityType.Equity, Symbols.IBM, 100, stopPrice: 10,
limitPrice: 11, 0, trailingAmount: 0.25m, trailingAsPercentage, limitOffset: 1, DateTime.UtcNow, "");
algorithm.Transactions.AddOrder(orderRequest);
var ticket = new OrderTicket(algorithm.Transactions, orderRequest);
tickets.Add(ticket);

var split = new Split(Symbols.IBM, orderTime, 1, 0.5m, SplitType.SplitOccurred);
_defaultBrokerageModel.ApplySplit(tickets, split);
transactionHandler.ProcessSynchronousEvents();

var order = algorithm.Transactions.GetOrders().Single();

Assert.AreEqual(5, order.GetPropertyValue("StopPrice", Flags.Instance | Flags.Public));
Assert.AreEqual(trailingAsPercentage ? 0.25m : 0.125m, order.GetPropertyValue("TrailingAmount"));
}

private static Order GetMarketOnOpenOrder()
{
Expand Down

0 comments on commit 2f9290b

Please sign in to comment.