|
| 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