Вопрос или проблема
Цель этого кода заключается в открытии сделки по модели разворота. Для длинной позиции это должно быть как минимум одна медвежья свеча, за которой следуют две бычьи свечи. Вход происходит на закрытии второй бычьей свечи. Этот код, похоже, игнорирует направляющие параметры и вместо этого открывает длинные позиции по большинству бычьих свечей, следующих за медвежьими, и короткие позиции по медвежьим, которые идут после бычьих.
using System;
using NinjaTrader.Cbi;
using NinjaTrader.NinjaScript;
#endregion
namespace NinjaTrader.NinjaScript.Strategies
{
public class BullishBearishAtmStrategy : Strategy
{
private string atmStrategyId = string.Empty;
private string orderId = string.Empty;
private bool isAtmCreated = false; // Переименованная переменная для избежания неоднозначности
private bool waitingForNextSetup = false;
protected override void OnStateChange()
{
if (State == State.SetDefaults)
{
Description = "Открыть длинную позицию после последовательности бычья-бычья-медвежья и короткую после последовательности медвежья-медвежья-бычья, используя стратегию ATM 'BooDeng'.";
Name = "BullishBearishAtmStrategy";
Calculate = Calculate.OnEachTick;
IsInstantiatedOnEachOptimizationIteration = true;
EntriesPerDirection = 1;
EntryHandling = EntryHandling.AllEntries;
IsExitOnSessionCloseStrategy = true;
ExitOnSessionCloseSeconds = 30;
BarsRequiredToTrade = 3;
}
}
protected override void OnBarUpdate()
{
if (CurrentBars[0] < 3 || State == State.Historical)
return;
// Проверка условий для длинной позиции
bool currentBullish = Close[0] > Open[0];
bool previousBullish = Close[1] > Open[1];
bool secondPreviousBearish = Close[2] < Open[2];
// Проверка условий для короткой позиции
bool currentBearish = Close[0] < Open[0];
bool previousBearish = Close[1] < Open[1];
bool secondPreviousBullish = Close[2] > Open[2];
// Предотвращение множественных входов в одном направлении, ожидая завершения установки
if (waitingForNextSetup && Position.MarketPosition == MarketPosition.Flat)
{
waitingForNextSetup = false;
}
// Проверка на наличие валидного условия для длинного входа
if (!waitingForNextSetup && currentBullish && previousBullish && secondPreviousBearish && Position.MarketPosition == MarketPosition.Flat && orderId.Length == 0 && atmStrategyId.Length == 0)
{
// Сброс флага создания стратегии ATM
isAtmCreated = false;
atmStrategyId = GetAtmStrategyUniqueId();
orderId = GetAtmStrategyUniqueId();
AtmStrategyCreate(OrderAction.Buy, OrderType.Market, 0, 0, TimeInForce.Gtc, orderId, "BooDeng", atmStrategyId, (atmCallbackErrorCode, atmCallBackId) => {
if (atmCallbackErrorCode == ErrorCode.NoError && atmCallBackId == atmStrategyId)
isAtmCreated = true;
});
// Установить флаг для предотвращения немедленного повторного входа
waitingForNextSetup = true;
}
// Проверка на наличие валидного условия для короткого входа
if (!waitingForNextSetup && currentBearish && previousBearish && secondPreviousBullish && Position.MarketPosition == MarketPosition.Flat && orderId.Length == 0 && atmStrategyId.Length == 0)
{
// Сброс флага создания стратегии ATM
isAtmCreated = false;
atmStrategyId = GetAtmStrategyUniqueId();
orderId = GetAtmStrategyUniqueId();
AtmStrategyCreate(OrderAction.Sell, OrderType.Market, 0, 0, TimeInForce.Gtc, orderId, "BooDeng", atmStrategyId, (atmCallbackErrorCode, atmCallBackId) => {
if (atmCallbackErrorCode == ErrorCode.NoError && atmCallBackId == atmStrategyId)
isAtmCreated = true;
});
// Установить флаг для предотвращения немедленного повторного входа
waitingForNextSetup = true;
}
// Проверка, была ли создана стратегия ATM
if (!isAtmCreated)
return;
// Проверка на наличие ожидающего входного ордера
if (orderId.Length > 0)
{
string[] status = GetAtmStrategyEntryOrderStatus(orderId);
// Если функция статуса не может найти указанный ордер, длина возвращаемого массива будет ноль
if (status.Length > 0)
{
Print("Средняя цена заполнения входного ордера: " + status[0]);
Print("Заполненное количество входного ордера: " + status[1]);
Print("Состояние входного ордера: " + status[2]);
// Если состояние ордера является терминальным, сбросьте значение идентификатора ордера
if (status[2] == "Filled" || status[2] == "Cancelled" || status[2] == "Rejected")
orderId = string.Empty;
}
}
else if (atmStrategyId.Length > 0 && GetAtmStrategyMarketPosition(atmStrategyId) == MarketPosition.Flat)
atmStrategyId = string.Empty;
if (atmStrategyId.Length > 0)
{
// Опционально, измените стоп/цель или отслеживайте информацию
Print("Текущая рыночная позиция стратегии ATM: " + GetAtmStrategyMarketPosition(atmStrategyId));
Print("Текущее количество по позиции стратегии ATM: " + GetAtmStrategyPositionQuantity(atmStrategyId));
Print("Текущая средняя цена стратегии ATM: " + GetAtmStrategyPositionAveragePrice(atmStrategyId));
Print("Нереализованная прибыль и убыток стратегии ATM: " + GetAtmStrategyUnrealizedProfitLoss(atmStrategyId));
}
}
}
}
Я пытался манипулировать значениями больше/меньше. Я настроил параметры с помощью bool и && и не нашел желаемого результата.
Ответ или решение
Код вашей стратегии на C# для NinjaTrader, который вы представили, имеет несколько проблем, связанных с логикой проверки условий для входа в позиции. Давайте разберем основные проблемы и предложим их решения.
Проблемы в коде
-
Логическая ошибка при ожидании следующего сетапа:
Вы используете переменнуюwaitingForNextSetup
для ограничения повторного входа в ту же позицию одновременно. Однако вы сбрасываете это значение, только если позиция становится "плоской" (MarketPosition.Flat
). Это приводит к тому, что может не обрабатываться корректный выход из позиции, и стратегия может заходить в новые позиции, даже если это не должно произойти. -
Отсутствие должной последовательности для проверки условий:
Вы проверяете условия для входа в длинную и короткую позиции почти в одном и том же блоке кода, что может привести к выполнению обоих условий, что нежелательно. Ваша логика требует более строгой проверки условий, чтобы предотвратить случайный вход на основании неполных последовательностей. -
Проблема с состоянием инициализации:
Долгосрочное состояние (isAtmCreated
) и наличие активного ордера должны быть четко разделены, чтобы избежать путаницы между текущими открытыми и закрытыми позициями.
Рекомендуемые изменения
Вот обновленный код с исправлениями:
protected override void OnBarUpdate()
{
if (CurrentBars[0] < 3 || State == State.Historical)
return;
// Логика проверки длинной позиции
bool currentBullish = Close[0] > Open[0];
bool previousBullish = Close[1] > Open[1];
bool secondPreviousBearish = Close[2] < Open[2];
// Логика проверки короткой позиции
bool currentBearish = Close[0] < Open[0];
bool previousBearish = Close[1] < Open[1];
bool secondPreviousBullish = Close[2] > Open[2];
// Проверка на допустимость длинной позиции
if (!waitingForNextSetup && currentBullish && previousBullish && secondPreviousBearish
&& Position.MarketPosition == MarketPosition.Flat && string.IsNullOrEmpty(orderId))
{
ExecuteLongTrade();
}
// Проверка на допустимость короткой позиции
if (!waitingForNextSetup && currentBearish && previousBearish && secondPreviousBullish
&& Position.MarketPosition == MarketPosition.Flat && string.IsNullOrEmpty(orderId))
{
ExecuteShortTrade();
}
// Обработчик состояния ордера
HandleOrderStatus();
}
private void ExecuteLongTrade()
{
atmStrategyId = GetAtmStrategyUniqueId();
orderId = GetAtmStrategyUniqueId();
AtmStrategyCreate(OrderAction.Buy, OrderType.Market, 0, 0, TimeInForce.Gtc, orderId, "BooDeng", atmStrategyId, (atmCallbackErrorCode, atmCallBackId) =>
{
if (atmCallbackErrorCode == ErrorCode.NoError && atmCallBackId == atmStrategyId)
{
isAtmCreated = true;
waitingForNextSetup = true; // Задержка на следующий сетап
}
});
}
private void ExecuteShortTrade()
{
atmStrategyId = GetAtmStrategyUniqueId();
orderId = GetAtmStrategyUniqueId();
AtmStrategyCreate(OrderAction.Sell, OrderType.Market, 0, 0, TimeInForce.Gtc, orderId, "BooDeng", atmStrategyId, (atmCallbackErrorCode, atmCallBackId) =>
{
if (atmCallbackErrorCode == ErrorCode.NoError && atmCallBackId == atmStrategyId)
{
isAtmCreated = true;
waitingForNextSetup = true; // Задержка на следующий сетап
}
});
}
private void HandleOrderStatus()
{
// Логика обработки статуса ордера
if (!string.IsNullOrEmpty(orderId))
{
string[] status = GetAtmStrategyEntryOrderStatus(orderId);
if (status.Length > 0)
{
Print("Статус ордера: " + status[2]);
if (status[2] == "Filled" || status[2] == "Cancelled" || status[2] == "Rejected")
orderId = string.Empty;
}
}
else if (!string.IsNullOrEmpty(atmStrategyId) && GetAtmStrategyMarketPosition(atmStrategyId) == MarketPosition.Flat)
{
atmStrategyId = string.Empty; // Сбросить атрибут стратегий, когда позиция закрыта
waitingForNextSetup = false; // Разрешить новый сетап
}
}
Объяснение изменений
-
Структурирование кода:
Разделил логику выполнения длинной и короткой торговли в отдельные функции (ExecuteLongTrade
иExecuteShortTrade
) для улучшения читаемости и управления логикой. -
Отдельная проверка статуса ордера:
ФункцияHandleOrderStatus
теперь обрабатывает статус ордера, что позволяет сделать код более организованным. -
Безопасное управление состоянием:
После выполнения ордераwaitingForNextSetup
устанавливается вtrue
, что предотвращает дальнейший вход до тех пор, пока текущая позиция не будет закрыта.
Эти изменения должны помочь вам достичь желаемой логики для торговли на основе паттернов свечей, что позволит избежать случайных входов в позиции.