Точнее - QUIK'овский take profit.
Давно уже написал обычным циклом, а тут вдруг решил оптимизировать по скорости.
В результате ускорил, как обычно, в 60 раз :)
Амиброкеровский тестер пишет, что метод выполняется 1.2 ms
Работает только последовательно (используются статические поля), потому что амиброкер всё-равно их последовательно запускает во время тестирования или оптимизации.
Кто заметит ошибку, пишите.
using System;
using AmiBroker;
using AmiBroker.PlugIn;
using AmiBroker.Utils;
namespace VGnAmiBrokerPlugIn
{
public static class TrailingStopUnsafe
{
private static ATArray _trailArray;
private static ATArray _close;
private static ATArray _low;
private static ATArray _high;
private static ATArray _buy;
private static ATArray _sell;
private static ATArray _short;
private static ATArray _cover;
private static ATArray _sellPrice;
private static ATArray _coverPrice;
//todo: только long или только short
///
/// Подтягивание стопа с фиксированным начальным размером
///
///
[ABMethod(Name = "VGnTprof")]
[ABParameter(0, Type = ABParameterType.Array, Description = "startStop")]
[ABParameter(1, Type = ABParameterType.Array, Description = "stopVal")]
[ABParameter(2, Type = ABParameterType.Array, Description = "profitVal")]
public static unsafe ATVar VGnTprof(int argNum, ATVar* argList)
{
try
{
var startStop = argList[0].GetArray();
var stopVal = argList[1].GetArray();
var profitVal = argList[2].GetArray();
_trailArray = new ATArray(0);
_close = ABHost.GetStockArray(StockField.Close);
_low = ABHost.GetStockArray(StockField.Low);
_high = ABHost.GetStockArray(StockField.High);
_buy = ABVars.Buy.GetArray();
_sell = ABVars.Sell.GetArray();
_short = ABVars.Short.GetArray();
_cover = ABVars.Cover.GetArray();
_sellPrice = ABVars.SellPrice.GetArray();
_coverPrice = ABVars.CoverPrice.GetArray();
DoLongTrail(startStop, stopVal, profitVal);
DoShortTrail(startStop, stopVal, profitVal);
}
catch (Exception e)
{
// present error message on indicator panel and the Log-Trace window
YException.Show("Error while executing indicator.", e);
}
return new ATVar(_trailArray);
}
///
///
///
///
///
///
///
Использовать startStop как значение цены или как размер стопа
private static unsafe void DoLongTrail(
ATArray startStop,
ATArray stopVal,
ATArray profitVal,
bool asPrice = false)
{
unchecked
{
var trailing = false;
float trail = 0;
float profit = 0;
// позиционирование на начало массивов
var pTrail = _trailArray.Array;
var pStopVal = stopVal.Array;
var pStartStop = startStop.Array;
var pProfitVal = profitVal.Array;
var pClose = _close.Array;
var pLow = _low.Array;
var pHigh = _high.Array;
var pBuy = _buy.Array;
var pSell = _sell.Array;
var pShort = _short.Array;
var pSellPrice = _sellPrice.Array;
for (var pTrailEnd = _trailArray.Array + _trailArray.Length;
pTrail < pTrailEnd;
pTrail++, pStopVal++, pStartStop++, pProfitVal++, pClose++, pLow++, pHigh++, pBuy++, pSell++, pShort++, pSellPrice++)
{
if (trailing)
{
*pBuy = ATFloat.False; // удаление лишнего сигнала открытия позиции
if (ATFloat.IsTrue(*pSell) || ATFloat.IsTrue(*pShort))
// прекращение отслеживания при получении обратного сигнала
{
trailing = false;
continue;
}
if (*pLow < trail) // срабатывание стопа
{
*pSell = ATFloat.True;
*pSellPrice = trail;
trailing = false;
continue;
}
if (*pHigh > profit)
trail = Math.Max(*pHigh - *pStopVal, trail); // слежение
}
else
{
*pSell = ATFloat.False; // удаление лишнего сигнала закрытия позиции
if (ATFloat.IsTrue(*pBuy)) // запуск отслеживания при открытии позиции
{
trail = asPrice ? *pStartStop : *pHigh - *pStartStop;
profit = *pHigh + *pProfitVal;
trailing = true;
}
}
if (trailing)
*pTrail = trail;
}
}
}
///
///
///
///
///
///
///
Использовать startStop как значение цены или как размер стопа
private static unsafe void DoShortTrail(
ATArray startStop,
ATArray stopVal,
ATArray profitVal,
bool asPrice = false)
{
unchecked
{
var trailing = false;
float trail = 0;
float profit = 0;
var pTrail = _trailArray.Array;
var pStopVal = stopVal.Array;
var pStartStop = startStop.Array;
var pProfitVal = profitVal.Array;
var pClose = _close.Array;
var pLow = _low.Array;
var pHigh = _high.Array;
var pBuy = _buy.Array;
var pShort = _short.Array;
var pCover = _cover.Array;
var pCoverPrice = _coverPrice.Array;
for (var pTrailEnd = _trailArray.Array + _trailArray.Length;
pTrail < pTrailEnd;
pTrail++, pStopVal++, pStartStop++, pProfitVal++, pClose++, pLow++, pHigh++, pBuy++, pCover++, pShort++, pCoverPrice++)
{
if (trailing)
{
*pShort = ATFloat.False; // remove excess buy signals
if (ATFloat.IsTrue(*pCover) || ATFloat.IsTrue(*pBuy))
{
trailing = false;
continue;
}
if (*pHigh > trail)
{
*pCover = ATFloat.True;
*pCoverPrice = trail;
trailing = false;
continue;
}
if (*pLow < profit)
trail = Math.Min(*pLow + *pStopVal, trail);
}
else
{
*pCover = ATFloat.False;
if (ATFloat.IsTrue(*pShort))
{
trail = asPrice ? *pStartStop : *pLow + *pStartStop;
profit = *pLow - *pProfitVal;
trailing = true;
}
}
if (trailing)
*pTrail = trail;
}
}
}
}
}