Skip to content

Commit a41caab

Browse files
authored
Add advanced order types and OCO support (#72)
1 parent 29214a5 commit a41caab

20 files changed

+1948
-60
lines changed

docs/how-to/advanced-orders.md

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
# Advanced Orders
2+
3+
This guide covers the advanced order types available in FLOX, including conditional orders, OCO, and execution flags.
4+
5+
## Order Types Overview
6+
7+
| Type | Use Case |
8+
|------|----------|
9+
| `STOP_MARKET` | Stop loss, breakout entry |
10+
| `STOP_LIMIT` | Stop with price control |
11+
| `TAKE_PROFIT_MARKET` | Lock in profits |
12+
| `TAKE_PROFIT_LIMIT` | Lock in profits with price control |
13+
| `TRAILING_STOP` | Dynamic stop that follows price |
14+
| OCO | One-Cancels-Other for breakouts |
15+
16+
## Using Signal Factory Methods
17+
18+
### Stop Orders
19+
20+
```cpp
21+
// Stop market - triggers market order when price crosses trigger
22+
emitStopMarket(symbol, Side::SELL, Price::fromDouble(95.0), qty);
23+
24+
// Stop limit - triggers limit order when price crosses trigger
25+
emitStopLimit(symbol, Side::SELL,
26+
Price::fromDouble(95.0), // trigger
27+
Price::fromDouble(94.5), // limit price
28+
qty);
29+
```
30+
31+
**Stop trigger logic:**
32+
- SELL stop: triggers when price <= triggerPrice (falling)
33+
- BUY stop: triggers when price >= triggerPrice (rising)
34+
35+
### Take Profit Orders
36+
37+
```cpp
38+
// Take profit market - exits position at profit target
39+
emitTakeProfitMarket(symbol, Side::SELL, Price::fromDouble(110.0), qty);
40+
41+
// Take profit limit
42+
emitTakeProfitLimit(symbol, Side::SELL,
43+
Price::fromDouble(110.0), // trigger
44+
Price::fromDouble(109.5), // limit price
45+
qty);
46+
```
47+
48+
**Take profit trigger logic:**
49+
- SELL TP: triggers when price >= triggerPrice (rising, lock profit on long)
50+
- BUY TP: triggers when price <= triggerPrice (falling, lock profit on short)
51+
52+
### Trailing Stop
53+
54+
```cpp
55+
// Fixed offset trailing stop (follows price by 5.0)
56+
emitTrailingStop(symbol, Side::SELL, Price::fromDouble(5.0), qty);
57+
58+
// Percentage trailing stop (follows price by 2%)
59+
emitTrailingStopPercent(symbol, Side::SELL, 200, qty); // 200 bps = 2%
60+
```
61+
62+
**Trailing stop behavior:**
63+
- SELL trailing: trigger follows price UP (never down)
64+
- When price drops to trigger -> order executes
65+
66+
## Time-In-Force Options
67+
68+
```cpp
69+
// IOC (Immediate-Or-Cancel) - fill immediately or cancel
70+
emitLimitBuy(symbol, price, qty, TimeInForce::IOC);
71+
72+
// FOK (Fill-Or-Kill) - fill completely or reject
73+
emitLimitBuy(symbol, price, qty, TimeInForce::FOK);
74+
75+
// POST_ONLY - maker only, reject if would take
76+
emitLimitBuy(symbol, price, qty, TimeInForce::POST_ONLY);
77+
```
78+
79+
## Execution Flags
80+
81+
### Using Signal Modifiers
82+
83+
```cpp
84+
auto signal = Signal::limitBuy(symbol, price, qty, orderId)
85+
.withTimeInForce(TimeInForce::IOC)
86+
.withReduceOnly()
87+
.withPostOnly();
88+
emit(signal);
89+
```
90+
91+
### Close Position
92+
93+
```cpp
94+
// Close entire position with reduce-only market order
95+
emitClosePosition(symbol);
96+
```
97+
98+
## Checking Exchange Capabilities
99+
100+
Before using advanced features, check if they're supported:
101+
102+
```cpp
103+
void MyStrategy::onStart() {
104+
auto caps = engine().executor().capabilities();
105+
106+
_useTrailingStop = caps.supports(OrderType::TRAILING_STOP);
107+
_useOCO = caps.supportsOCO;
108+
109+
if (!_useTrailingStop) {
110+
log().warn("Trailing stop not supported, using manual logic");
111+
}
112+
}
113+
```
114+
115+
## Example: Manual TP/SL Management
116+
117+
Since strategies have full control over order lifecycle, you can implement custom TP/SL logic:
118+
119+
```cpp
120+
void onOrderFilled(const Order& entryOrder) {
121+
if (entryOrder.id == _entryOrderId) {
122+
Price entryPrice = entryOrder.price;
123+
124+
// Place TP
125+
_tpOrderId = emitTakeProfitMarket(
126+
entryOrder.symbol, Side::SELL,
127+
Price::fromDouble(entryPrice.toDouble() * 1.06), // 6% profit
128+
entryOrder.quantity);
129+
130+
// Place SL
131+
_slOrderId = emitStopMarket(
132+
entryOrder.symbol, Side::SELL,
133+
Price::fromDouble(entryPrice.toDouble() * 0.98), // 2% stop
134+
entryOrder.quantity);
135+
}
136+
137+
// When TP fills, cancel SL
138+
if (entryOrder.id == _tpOrderId) {
139+
emitCancel(_slOrderId);
140+
}
141+
142+
// When SL fills, cancel TP
143+
if (entryOrder.id == _slOrderId) {
144+
emitCancel(_tpOrderId);
145+
}
146+
}
147+
```
148+
149+
## Example: Trailing Stop for Profit Protection
150+
151+
```cpp
152+
void onOrderFilled(const Order& order) {
153+
if (order.side == Side::BUY) {
154+
// Place trailing stop to protect profits
155+
emitTrailingStopPercent(order.symbol, Side::SELL, 300, order.quantity);
156+
}
157+
}
158+
```
159+
160+
## See Also
161+
162+
* [Order Types](../reference/api/common.md#ordertype)
163+
* [Time-In-Force](../reference/api/common.md#timeinforce)
164+
* [Order Structure](../reference/api/execution/order.md)
165+
* [Exchange Capabilities](../reference/api/execution/exchange_capabilities.md)

docs/reference/api/common.md

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,51 @@ enum class OptionType {
3333
Represents the execution style of an order.
3434

3535
```cpp
36-
enum class OrderType {
37-
LIMIT, // Limit order
38-
MARKET // Market order
36+
enum class OrderType : uint8_t {
37+
LIMIT = 0, // Limit order
38+
MARKET = 1, // Market order
39+
STOP_MARKET = 2, // Stop market (triggers market order when price crosses trigger)
40+
STOP_LIMIT = 3, // Stop limit (triggers limit order when price crosses trigger)
41+
TAKE_PROFIT_MARKET = 4, // Take profit market (triggers market order at profit target)
42+
TAKE_PROFIT_LIMIT = 5, // Take profit limit (triggers limit order at profit target)
43+
TRAILING_STOP = 6, // Trailing stop (trigger follows price movement)
44+
ICEBERG = 7, // Iceberg (shows only partial quantity)
3945
};
4046
```
4147
48+
| Type | Description |
49+
|------|-------------|
50+
| `LIMIT` | Standard limit order, placed in the order book |
51+
| `MARKET` | Executes immediately at best available price |
52+
| `STOP_MARKET` | Market order triggered when price crosses trigger price |
53+
| `STOP_LIMIT` | Limit order triggered when price crosses trigger price |
54+
| `TAKE_PROFIT_MARKET` | Market order triggered at profit target |
55+
| `TAKE_PROFIT_LIMIT` | Limit order triggered at profit target |
56+
| `TRAILING_STOP` | Stop that follows favorable price movement |
57+
| `ICEBERG` | Shows only visible portion of total quantity |
58+
59+
### `TimeInForce`
60+
61+
Specifies how long an order remains active.
62+
63+
```cpp
64+
enum class TimeInForce : uint8_t {
65+
GTC = 0, // Good Till Cancel (default)
66+
IOC = 1, // Immediate Or Cancel
67+
FOK = 2, // Fill Or Kill
68+
GTD = 3, // Good Till Date
69+
POST_ONLY = 4, // Maker only (rejected if would take)
70+
};
71+
```
72+
73+
| Policy | Description |
74+
|--------|-------------|
75+
| `GTC` | Remains active until filled or explicitly canceled |
76+
| `IOC` | Fills immediately (partially or fully), cancels remainder |
77+
| `FOK` | Fills completely or rejects entirely |
78+
| `GTD` | Active until specified expiration time |
79+
| `POST_ONLY` | Rejected if would immediately match (maker only) |
80+
4281
### `Side`
4382

4483
Represents the direction of an order.

docs/reference/api/execution/abstract_execution_listener.md

Lines changed: 60 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,28 @@ public:
1010

1111
SubscriberId id() const override;
1212

13-
virtual void onOrderSubmitted(const Order& order) = 0;
14-
virtual void onOrderAccepted(const Order& order) = 0;
15-
virtual void onOrderPartiallyFilled(const Order& order, Quantity fillQty) = 0;
16-
virtual void onOrderFilled(const Order& order) = 0;
17-
virtual void onOrderCanceled(const Order& order) = 0;
18-
virtual void onOrderExpired(const Order& order) = 0;
19-
virtual void onOrderRejected(const Order& order, const std::string& reason) = 0;
20-
virtual void onOrderReplaced(const Order& oldOrder, const Order& newOrder) = 0;
13+
// Standard order events
14+
virtual void onOrderSubmitted(const Order& order) {}
15+
virtual void onOrderAccepted(const Order& order) {}
16+
virtual void onOrderPartiallyFilled(const Order& order, Quantity fillQty) {}
17+
virtual void onOrderFilled(const Order& order) {}
18+
virtual void onOrderCanceled(const Order& order) {}
19+
virtual void onOrderExpired(const Order& order) {}
20+
virtual void onOrderRejected(const Order& order, const std::string& reason) {}
21+
virtual void onOrderReplaced(const Order& oldOrder, const Order& newOrder) {}
22+
23+
// Conditional order events
24+
virtual void onOrderPendingTrigger(const Order& order) {}
25+
virtual void onOrderTriggered(const Order& order) {}
26+
virtual void onTrailingStopUpdated(const Order& order, Price newTriggerPrice) {}
2127
};
2228
```
2329
2430
## Purpose
2531
2632
* Provide a type-safe listener interface for receiving detailed updates on order status transitions.
2733
28-
## Responsibilities
34+
## Standard Order Events
2935
3036
| Method | Triggered On |
3137
| ------------------------ | -------------------------------------------------- |
@@ -38,8 +44,53 @@ public:
3844
| `onOrderRejected` | Rejected by exchange or risk engine (with reason). |
3945
| `onOrderReplaced` | Order was replaced with a new one. |
4046
47+
## Conditional Order Events
48+
49+
| Method | Triggered On |
50+
| ------------------------ | -------------------------------------------------- |
51+
| `onOrderPendingTrigger` | Conditional order waiting for trigger condition. |
52+
| `onOrderTriggered` | Trigger condition met, order converted to market/limit. |
53+
| `onTrailingStopUpdated` | Trailing stop trigger price moved. |
54+
55+
### Conditional Order Flow
56+
57+
```
58+
submitOrder(STOP_MARKET)
59+
60+
v
61+
onOrderSubmitted() → onOrderAccepted() → onOrderPendingTrigger()
62+
63+
│ (price crosses trigger)
64+
v
65+
onOrderTriggered() → [order converts to MARKET] → onOrderFilled()
66+
```
67+
68+
### Trailing Stop Flow
69+
70+
```
71+
submitOrder(TRAILING_STOP)
72+
73+
v
74+
onOrderPendingTrigger()
75+
76+
│ (price moves favorably)
77+
v
78+
onTrailingStopUpdated(newPrice) → onTrailingStopUpdated(newPrice) → ...
79+
80+
│ (price reverses to trigger)
81+
v
82+
onOrderTriggered() → onOrderFilled()
83+
```
84+
4185
## Notes
4286
4387
* Each listener is identified via a stable `SubscriberId`.
4488
* Used in tandem with `OrderEvent::dispatchTo()` to decouple producers from listeners.
4589
* Implemented by components such as `PositionManager`, `ExecutionTracker`, and metrics/reporting modules.
90+
* All methods have default empty implementations.
91+
92+
## See Also
93+
94+
* [OrderEvent](events/order_event.md) — Event structure
95+
* [OrderExecutionBus](bus/order_execution_bus.md) — Event bus
96+
* [Order](order.md) — Order structure

0 commit comments

Comments
 (0)