Skip to content

Commit bc6262d

Browse files
authored
Add support for stop limit orders (#22)
* Add support for stop limit orders * Minor change * Minor test change
1 parent 93bdc78 commit bc6262d

File tree

3 files changed

+59
-13
lines changed

3 files changed

+59
-13
lines changed

QuantConnect.BitfinexBrokerage.Tests/BitfinexBrokerageTests.cs

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
using QuantConnect.Brokerages;
2424
using QuantConnect.Tests.Common.Securities;
2525
using QuantConnect.Lean.Engine.DataFeeds;
26+
using QuantConnect.Orders;
2627

2728
namespace QuantConnect.Tests.Brokerages.Bitfinex
2829
{
@@ -37,9 +38,10 @@ public partial class BitfinexBrokerageTests : BrokerageTests
3738
/// <returns></returns>
3839
protected override IBrokerage CreateBrokerage(IOrderProvider orderProvider, ISecurityProvider securityProvider)
3940
{
41+
var security = securityProvider.GetSecurity(Symbol);
4042
var securities = new SecurityManager(new TimeKeeper(DateTime.UtcNow, TimeZones.NewYork))
4143
{
42-
{Symbol, SecurityProvider.GetSecurity(Symbol)}
44+
{Symbol, security}
4345
};
4446

4547
var transactions = new SecurityTransactionManager(null, securities);
@@ -48,8 +50,9 @@ protected override IBrokerage CreateBrokerage(IOrderProvider orderProvider, ISec
4850

4951
var algorithm = new Mock<IAlgorithm>();
5052
algorithm.Setup(a => a.Transactions).Returns(transactions);
51-
algorithm.Setup(a => a.BrokerageModel).Returns(new BitfinexBrokerageModel());
53+
algorithm.Setup(a => a.BrokerageModel).Returns(new BitfinexBrokerageModel(AccountType.Cash));
5254
algorithm.Setup(a => a.Portfolio).Returns(new SecurityPortfolioManager(securities, transactions, algorithmSettings));
55+
algorithm.Setup(a => a.Securities).Returns(securities);
5356

5457
return new BitfinexBrokerage(
5558
Config.Get("bitfinex-api-key"),
@@ -64,13 +67,21 @@ protected override IBrokerage CreateBrokerage(IOrderProvider orderProvider, ISec
6467
/// Gets the symbol to be traded, must be shortable
6568
/// </summary>
6669
protected override Symbol Symbol => StaticSymbol;
67-
private static Symbol StaticSymbol => Symbol.Create("ETHUSD", SecurityType.Crypto, Market.Bitfinex);
70+
private static Symbol StaticSymbol => Symbol.Create("TESTBTCTESTUSD", SecurityType.Crypto, Market.Bitfinex);
6871

6972
/// <summary>
7073
/// Gets the security type associated with the <see cref="BrokerageTests.Symbol" />
7174
/// </summary>
7275
protected override SecurityType SecurityType => SecurityType.Crypto;
7376

77+
private static TestCaseData[] OrderParameters =>
78+
[
79+
new TestCaseData(new MarketOrderTestParameters(StaticSymbol)),
80+
new TestCaseData(new LimitOrderTestParameters(StaticSymbol, 1000m, 100m)),
81+
new TestCaseData(new StopMarketOrderTestParameters(StaticSymbol, 1000m, 100m)),
82+
new TestCaseData(new StopLimitOrderTestParameters(StaticSymbol, 1000m, 100m)),
83+
];
84+
7485
/// <summary>
7586
/// Gets the current market price of the specified security
7687
/// </summary>
@@ -95,43 +106,61 @@ protected override decimal GetAskPrice(Symbol symbol)
95106
/// </summary>
96107
protected override decimal GetDefaultQuantity() => 0.04m;
97108

98-
[Explicit("Ignore a test")]
109+
[TestCaseSource(nameof(OrderParameters))]
99110
public override void CancelOrders(OrderTestParameters parameters)
100111
{
101112
base.CancelOrders(parameters);
102113
}
103114

104-
[Explicit("Ignore a test")]
115+
[TestCaseSource(nameof(OrderParameters))]
105116
public override void LongFromZero(OrderTestParameters parameters)
106117
{
107118
base.LongFromZero(parameters);
119+
120+
if (parameters is not MarketOrderTestParameters)
121+
{
122+
// We expect the orders to be open
123+
var openOrders = Brokerage.GetOpenOrders();
124+
Assert.AreEqual(1, openOrders.Count);
125+
126+
var expectedOrderType = parameters switch
127+
{
128+
LimitOrderTestParameters _ => typeof(LimitOrder),
129+
StopMarketOrderTestParameters _ => typeof(StopMarketOrder),
130+
StopLimitOrderTestParameters _ => typeof(StopLimitOrder),
131+
_ => throw new ArgumentException("Unsupported order type for this test", nameof(parameters))
132+
};
133+
134+
// Check that the order type matches the expected type
135+
Assert.IsInstanceOf(expectedOrderType, openOrders[0]);
136+
}
108137
}
109138

110-
[Explicit("Ignore a test")]
139+
[TestCaseSource(nameof(OrderParameters))]
111140
public override void CloseFromLong(OrderTestParameters parameters)
112141
{
113142
base.CloseFromLong(parameters);
114143
}
115144

116-
[Explicit("Ignore a test")]
145+
[TestCaseSource(nameof(OrderParameters))]
117146
public override void ShortFromZero(OrderTestParameters parameters)
118147
{
119148
base.ShortFromZero(parameters);
120149
}
121150

122-
[Explicit("Ignore a test")]
151+
[TestCaseSource(nameof(OrderParameters))]
123152
public override void CloseFromShort(OrderTestParameters parameters)
124153
{
125154
base.CloseFromShort(parameters);
126155
}
127156

128-
[Explicit("Ignore a test")]
157+
[TestCaseSource(nameof(OrderParameters))]
129158
public override void ShortFromLong(OrderTestParameters parameters)
130159
{
131160
base.ShortFromLong(parameters);
132161
}
133162

134-
[Explicit("Ignore a test")]
163+
[TestCaseSource(nameof(OrderParameters))]
135164
public override void LongFromShort(OrderTestParameters parameters)
136165
{
137166
base.LongFromShort(parameters);

QuantConnect.BitfinexBrokerage/BitfinexBrokerage.Utility.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,10 @@ private static string ConvertOrderType(AccountType accountType, OrderType orderT
186186
outputOrderType = "STOP";
187187
break;
188188

189+
case OrderType.StopLimit:
190+
outputOrderType = "STOP LIMIT";
191+
break;
192+
189193
default:
190194
throw new NotSupportedException($"BitfinexBrokerage.ConvertOrderType: Unsupported order type: {orderType}");
191195
}
@@ -213,6 +217,9 @@ private static decimal GetOrderPrice(Order order)
213217

214218
case OrderType.StopMarket:
215219
return ((StopMarketOrder)order).StopPrice;
220+
221+
case OrderType.StopLimit:
222+
return ((StopLimitOrder)order).LimitPrice;
216223
}
217224

218225
throw new NotSupportedException($"BitfinexBrokerage.ConvertOrderType: Unsupported order type: {order.Type}");

QuantConnect.BitfinexBrokerage/BitfinexBrokerage.cs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,11 @@ public override bool PlaceOrder(Order order)
7373
{ "price", GetOrderPrice(order).ToStringInvariant() }
7474
};
7575

76+
if (order is StopLimitOrder stopLimitOrder)
77+
{
78+
parameters.Add("price_aux_limit", stopLimitOrder.StopPrice.ToStringInvariant());
79+
}
80+
7681
var orderProperties = order.Properties as BitfinexOrderProperties;
7782
if (orderProperties != null)
7883
{
@@ -200,19 +205,24 @@ public override List<Order> GetOpenOrders()
200205
var price = item.Price;
201206
var symbol = _symbolMapper.GetLeanSymbol(item.Symbol, SecurityType.Crypto, Market.Bitfinex);
202207
var time = Time.UnixMillisecondTimeStampToDateTime(item.MtsCreate);
208+
var orderTypeStr = item.Type.Replace("EXCHANGE", "").Trim();
203209

204-
if (item.Type.Replace("EXCHANGE", "").Trim() == "MARKET")
210+
if (orderTypeStr == "MARKET")
205211
{
206212
order = new MarketOrder(symbol, quantity, time, price);
207213
}
208-
else if (item.Type.Replace("EXCHANGE", "").Trim() == "LIMIT")
214+
else if (orderTypeStr == "LIMIT")
209215
{
210216
order = new LimitOrder(symbol, quantity, price, time);
211217
}
212-
else if (item.Type.Replace("EXCHANGE", "").Trim() == "STOP")
218+
else if (orderTypeStr == "STOP")
213219
{
214220
order = new StopMarketOrder(symbol, quantity, price, time);
215221
}
222+
else if (orderTypeStr == "STOP LIMIT")
223+
{
224+
order = new StopLimitOrder(symbol, quantity, price, item.PriceAuxLimit, time);
225+
}
216226
else
217227
{
218228
OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Error, (int)response.StatusCode,

0 commit comments

Comments
 (0)