Skip to content

Commit caee6f6

Browse files
committed
docs: placing orders
1 parent 84f2029 commit caee6f6

File tree

2 files changed

+137
-0
lines changed

2 files changed

+137
-0
lines changed

.vitepress/config.mts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export default defineConfig({
2929
items: [
3030
{ text: 'Anatomy of a Strategy', link: '/anatomy' },
3131
{ text: 'Using Indicators', link: '/using-indicators' },
32+
{ text: 'Placing Orders', link: '/placing-orders' },
3233
]
3334
}
3435
],

placing-orders.md

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
# Placing Orders
2+
3+
Once your strategy has analyzed market data and decided to make a trade, the next step is to place an order. In Stochastix, all order management is handled through a set of convenient helper methods available in the `AbstractStrategy` class.
4+
5+
## The Order Signal & Asynchronous Execution
6+
7+
A critical concept to understand is that orders are not executed instantly on the same bar they are placed. Stochastix follows a realistic, asynchronous execution model to prevent look-ahead bias.
8+
9+
1. When you call `$this->entry()` or `$this->exit()` in your `onBar()` method, you are not placing the trade directly. Instead, you are creating an **`OrderSignal`** and queuing it for processing.
10+
2. The backtesting engine collects all signals generated during the current bar.
11+
3. At the beginning of the **next bar**, the `OrderManager` processes the queue and executes the trades. Market orders are typically filled at the `open` price of this next bar.
12+
13+
This ensures your strategy logic can only react to data that was available at the time of the decision.
14+
15+
## Entering a Position: `$this->entry()`
16+
17+
The `$this->entry()` method is your primary tool for opening a new position.
18+
19+
```php
20+
$this->entry(
21+
DirectionEnum $direction,
22+
OrderTypeEnum $orderType,
23+
float|string $quantity,
24+
float|string|null $price = null,
25+
?int $timeInForceBars = null,
26+
float|string|null $stopLossPrice = null,
27+
float|string|null $takeProfitPrice = null,
28+
?string $clientOrderId = null,
29+
array|string|null $enterTags = null
30+
): void
31+
```
32+
33+
**Key Parameters:**
34+
35+
* `direction`: The direction of the trade, either `DirectionEnum::Long` or `DirectionEnum::Short`.
36+
* `orderType`: The type of order to place.
37+
* `quantity`: The amount of the asset to buy or sell.
38+
* `price`: **Required for Limit and Stop orders**. For a `Limit` buy, this is the maximum price you're willing to pay. For a `Stop` buy, this is the price at which a market buy is triggered.
39+
* `stopLossPrice`/`takeProfitPrice`: Set automatic market exit triggers for the position once it's opened.
40+
* `enterTags`: An array of strings to label why this entry was triggered. These tags are shown in the backtest results for performance attribution.
41+
42+
### Order Type Examples
43+
44+
#### Market Order
45+
46+
The simplest order type. It will be executed at the open price of the next bar.
47+
48+
```php
49+
use Stochastix\Domain\Common\Enum\DirectionEnum;
50+
use Stochastix\Domain\Order\Enum\OrderTypeEnum;
51+
52+
// Go long with a market order
53+
$this->entry(
54+
direction: DirectionEnum::Long,
55+
orderType: OrderTypeEnum::Market,
56+
quantity: '0.5'
57+
);
58+
```
59+
60+
#### Limit Order
61+
62+
A pending order that executes only if the market price reaches a specified level or better.
63+
64+
```php
65+
// The current price is $105. We want to buy if it drops to $100.
66+
$limitPrice = '100.0';
67+
$currentPrice = '105.0';
68+
69+
if ($currentPrice > $limitPrice) {
70+
$this->entry(
71+
direction: DirectionEnum::Long,
72+
orderType: OrderTypeEnum::Limit,
73+
quantity: '0.5',
74+
price: $limitPrice, // The price must be provided
75+
clientOrderId: 'my-long-limit-1' // A unique ID is required for pending orders
76+
);
77+
}
78+
```
79+
80+
#### Stop Order (Stop-Market)
81+
82+
A pending order that becomes a market order once a specific price level is reached. This is often used to enter a breakout trade.
83+
84+
```php
85+
// The current price is $95. We want to buy if it breaks above the resistance at $100.
86+
$stopPrice = '100.0';
87+
88+
$this->entry(
89+
direction: DirectionEnum::Long,
90+
orderType: OrderTypeEnum::Stop,
91+
quantity: '0.5',
92+
price: $stopPrice, // The price that triggers the market order
93+
clientOrderId: 'my-breakout-buy-1'
94+
);
95+
```
96+
97+
## Checking State: `$this->isInPosition()`
98+
99+
Before entering a new trade, you should always check if you already have a position open for the current symbol. The `$this->isInPosition()` helper returns `true` or `false`.
100+
101+
```php
102+
if (!$this->isInPosition()) {
103+
// It's safe to check for a new entry signal
104+
if ($emaFast->crossesOver($emaSlow)) {
105+
$this->entry(DirectionEnum::Long, OrderTypeEnum::Market, '0.5');
106+
}
107+
}
108+
```
109+
110+
## Exiting a Position: `$this->exit()`
111+
112+
To close an open position, use the `$this->exit()` method. It queues a market order for the opposite direction of your current position. You must specify the quantity to close. To close the entire position, you can get the quantity from the current position object.
113+
114+
```php
115+
$openPosition = $this->orderManager->getPortfolioManager()->getOpenPosition($this->context->getCurrentSymbol());
116+
117+
if ($openPosition) { // Ensure a position actually exists
118+
if ($openPosition->direction === DirectionEnum::Long && $emaFast->crossesUnder($emaSlow)) {
119+
// Close the entire long position
120+
$this->exit($openPosition->quantity, exitTags: 'ema_cross_down');
121+
}
122+
}
123+
```
124+
125+
## Cancelling Pending Orders
126+
127+
If you have placed a `Limit` or `Stop` order that has not yet been filled, you can cancel it programmatically using the `clientOrderId` you provided when creating it.
128+
129+
```php
130+
// In a previous bar, we placed a limit order:
131+
// $this->entry(..., clientOrderId: 'my-long-limit-1');
132+
133+
// Now, market conditions have changed and we no longer want that order.
134+
$this->cancelOrder('my-long-limit-1');
135+
```
136+
This removes the order from the pending order book so it will not be triggered in the future.

0 commit comments

Comments
 (0)