179 lines
5.7 KiB
Markdown
179 lines
5.7 KiB
Markdown
# Task 1 — Wire NT8OrderAdapter.ExecuteInNT8()
|
||
|
||
**Priority:** CRITICAL
|
||
**Estimated time:** 3–4 hours
|
||
**Blocks:** All backtest and live trading
|
||
**Status:** TODO
|
||
|
||
---
|
||
|
||
## Problem
|
||
|
||
`NT8OrderAdapter.ExecuteInNT8()` in `src/NT8.Adapters/NinjaTrader/NT8OrderAdapter.cs` is a stub.
|
||
It only logs to an internal list. The actual NT8 calls (`EnterLong`, `EnterShort`, `SetStopLoss`, `SetProfitTarget`) are in a commented-out block and never execute. This is why backtests show zero trades.
|
||
|
||
---
|
||
|
||
## What Needs to Change
|
||
|
||
### File: `src/NT8.Adapters/NinjaTrader/NT8OrderAdapter.cs`
|
||
|
||
The adapter currently has no reference to the actual NinjaScript `Strategy` object. It needs a way to call NT8 managed order methods. The pattern used by `NT8StrategyBase` is the right model to follow.
|
||
|
||
**Option A (Recommended):** Inject a callback delegate so the adapter can call NT8 methods without directly holding a NinjaScript reference.
|
||
|
||
Add a new `INT8ExecutionBridge` interface:
|
||
```csharp
|
||
// new file: src/NT8.Adapters/NinjaTrader/INT8ExecutionBridge.cs
|
||
namespace NT8.Adapters.NinjaTrader
|
||
{
|
||
/// <summary>
|
||
/// Provides NT8OrderAdapter access to NinjaScript execution methods.
|
||
/// Implemented by NT8StrategyBase.
|
||
/// </summary>
|
||
public interface INT8ExecutionBridge
|
||
{
|
||
/// <summary>Submit a long entry with stop and target.</summary>
|
||
void EnterLongManaged(int quantity, string signalName, int stopTicks, int targetTicks, double tickSize);
|
||
|
||
/// <summary>Submit a short entry with stop and target.</summary>
|
||
void EnterShortManaged(int quantity, string signalName, int stopTicks, int targetTicks, double tickSize);
|
||
|
||
/// <summary>Exit all long positions.</summary>
|
||
void ExitLongManaged(string signalName);
|
||
|
||
/// <summary>Exit all short positions.</summary>
|
||
void ExitShortManaged(string signalName);
|
||
|
||
/// <summary>Flatten the full position immediately.</summary>
|
||
void FlattenAll();
|
||
}
|
||
}
|
||
```
|
||
|
||
Update `NT8OrderAdapter` constructor to accept `INT8ExecutionBridge`:
|
||
```csharp
|
||
public NT8OrderAdapter(INT8ExecutionBridge bridge)
|
||
{
|
||
if (bridge == null)
|
||
throw new ArgumentNullException("bridge");
|
||
_bridge = bridge;
|
||
_executionHistory = new List<NT8OrderExecutionRecord>();
|
||
}
|
||
```
|
||
|
||
Implement `ExecuteInNT8()`:
|
||
```csharp
|
||
private void ExecuteInNT8(StrategyIntent intent, SizingResult sizing)
|
||
{
|
||
if (intent == null)
|
||
throw new ArgumentNullException("intent");
|
||
if (sizing == null)
|
||
throw new ArgumentNullException("sizing");
|
||
|
||
var signalName = string.Format("SDK_{0}_{1}", intent.Symbol, intent.Side);
|
||
|
||
if (intent.Side == Common.Models.OrderSide.Buy)
|
||
{
|
||
_bridge.EnterLongManaged(
|
||
sizing.Contracts,
|
||
signalName,
|
||
intent.StopTicks,
|
||
intent.TargetTicks.HasValue ? intent.TargetTicks.Value : 0,
|
||
intent.TickSize);
|
||
}
|
||
else if (intent.Side == Common.Models.OrderSide.Sell)
|
||
{
|
||
_bridge.EnterShortManaged(
|
||
sizing.Contracts,
|
||
signalName,
|
||
intent.StopTicks,
|
||
intent.TargetTicks.HasValue ? intent.TargetTicks.Value : 0,
|
||
intent.TickSize);
|
||
}
|
||
|
||
lock (_lock)
|
||
{
|
||
_executionHistory.Add(new NT8OrderExecutionRecord(
|
||
intent.Symbol,
|
||
intent.Side,
|
||
intent.EntryType,
|
||
sizing.Contracts,
|
||
intent.StopTicks,
|
||
intent.TargetTicks,
|
||
DateTime.UtcNow));
|
||
}
|
||
}
|
||
```
|
||
|
||
### File: `src/NT8.Adapters/Strategies/NT8StrategyBase.cs`
|
||
|
||
Implement `INT8ExecutionBridge` on `NT8StrategyBase`:
|
||
```csharp
|
||
public class NT8StrategyBase : Strategy, INT8ExecutionBridge
|
||
{
|
||
public void EnterLongManaged(int quantity, string signalName, int stopTicks, int targetTicks, double tickSize)
|
||
{
|
||
SetStopLoss(signalName, CalculationMode.Ticks, stopTicks, false);
|
||
if (targetTicks > 0)
|
||
SetProfitTarget(signalName, CalculationMode.Ticks, targetTicks);
|
||
EnterLong(quantity, signalName);
|
||
}
|
||
|
||
public void EnterShortManaged(int quantity, string signalName, int stopTicks, int targetTicks, double tickSize)
|
||
{
|
||
SetStopLoss(signalName, CalculationMode.Ticks, stopTicks, false);
|
||
if (targetTicks > 0)
|
||
SetProfitTarget(signalName, CalculationMode.Ticks, targetTicks);
|
||
EnterShort(quantity, signalName);
|
||
}
|
||
|
||
public void ExitLongManaged(string signalName)
|
||
{
|
||
ExitLong(signalName);
|
||
}
|
||
|
||
public void ExitShortManaged(string signalName)
|
||
{
|
||
ExitShort(signalName);
|
||
}
|
||
|
||
// FlattenAll already called in NT8 as: this.Account.Flatten(Instrument)
|
||
// or: ExitLong(); ExitShort();
|
||
public void FlattenAll()
|
||
{
|
||
ExitLong("EmergencyFlatten");
|
||
ExitShort("EmergencyFlatten");
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Acceptance Criteria
|
||
|
||
- [ ] `NT8OrderAdapter` takes `INT8ExecutionBridge` in its constructor
|
||
- [ ] `ExecuteInNT8()` calls the bridge (no more commented-out code)
|
||
- [ ] `NT8StrategyBase` implements `INT8ExecutionBridge`
|
||
- [ ] `OnOrderUpdate()` callback in `NT8OrderAdapter` updates `BasicOrderManager` state (pass the fill back)
|
||
- [ ] `verify-build.bat` passes
|
||
- [ ] A backtest run on SimpleORBNT8 produces actual trades (not zero)
|
||
|
||
---
|
||
|
||
## Files to Create/Modify
|
||
|
||
| File | Action |
|
||
|---|---|
|
||
| `src/NT8.Adapters/NinjaTrader/INT8ExecutionBridge.cs` | CREATE |
|
||
| `src/NT8.Adapters/NinjaTrader/NT8OrderAdapter.cs` | MODIFY — implement `ExecuteInNT8()`, update constructor |
|
||
| `src/NT8.Adapters/Strategies/NT8StrategyBase.cs` | MODIFY — implement `INT8ExecutionBridge` |
|
||
|
||
---
|
||
|
||
## Do NOT Change
|
||
|
||
- `src/NT8.Core/OMS/BasicOrderManager.cs` — the OMS is correct
|
||
- `src/NT8.Strategies/Examples/SimpleORBStrategy.cs` — strategy logic is correct
|
||
- Any existing test files
|