Ejemplo de estrategia F#
La creación de una estrategia desde código fuente se demostrará con el ejemplo de la estrategia SMA, similar al ejemplo de estrategia SMA ensamblada desde cubos en la sección Creación de un algoritmo a partir de cubos.
Esta sección no describirá las construcciones del lenguaje F# (ni Strategy, que se usa como base para crear estrategias), pero mencionará características específicas para trabajar con código en Designer.
Tip
Las estrategias creadas en Designer son compatibles con las estrategias creadas en la API gracias al uso de la clase base común Strategy. Esto facilita mucho más la ejecución de dichas estrategias fuera de Designer que la ejecución de esquemas.
- Los parámetros de la estrategia se crean usando un enfoque especial:
// --- Parámetros de estrategia: CandleType, Long, Short, TakeValue, StopValue ---
// Parámetro para el tipo de vela
let candleTypeParam =
this.Param<DataType>(nameof(this.CandleType), DataType.TimeFrame(TimeSpan.FromMinutes 1.0))
.SetDisplay("Tipo de vela", "Tipo de vela para el cálculo de la estrategia.", "Configuración general")
// Parámetro para la SMA larga
let longParam =
this.Param<int>(nameof(this.Long), 80)
// Parámetro para la SMA corta
let shortParam =
this.Param<int>(nameof(this.Short), 30)
// Parámetro para take profit
let takeValueParam =
this.Param<Unit>(nameof(this.TakeValue), Unit(0m, UnitTypes.Absolute))
// Parámetro para stop loss
let stopValueParam =
this.Param<Unit>(nameof(this.StopValue), Unit(2m, UnitTypes.Percent))
// Bandera para seguir el cruce de SMA
let mutable isShortLessThenLong : bool option = None
// --------------------- Propiedades públicas ---------------------
/// <summary>Tipo de vela usado por la estrategia.</summary>
member this.CandleType
with get () = candleTypeParam.Value
and set value = candleTypeParam.Value <- value
/// <summary>Período para el indicador SMA largo.</summary>
member this.Long
with get () = longParam.Value
and set value = longParam.Value <- value
/// <summary>Período para el indicador SMA corto.</summary>
member this.Short
with get () = shortParam.Value
and set value = shortParam.Value <- value
/// <summary>Valor de take profit.</summary>
member this.TakeValue
with get () = takeValueParam.Value
and set value = takeValueParam.Value <- value
/// <summary>Valor de stop loss.</summary>
member this.StopValue
with get () = stopValueParam.Value
and set value = stopValueParam.Value <- value
Al usar la clase StrategyParam, se aplica automáticamente el enfoque de guardar y restaurar configuración.
- Al crear indicadores y suscribirse a datos de mercado, debe vincularlos para que los datos entrantes de la suscripción puedan actualizar los valores de los indicadores:
// ---------- Crear indicadores ----------
let longSma = SMA()
longSma.Length <- this.Long
let shortSma = SMA()
shortSma.Length <- this.Short
// ---------------------------------------
// ------ Suscribirse al flujo de velas y vincular indicadores ------
let subscription = this.SubscribeCandles(this.CandleType)
// Vincular nuestros indicadores a la suscripción y asignar la función de procesamiento
subscription
.Bind(longSma, shortSma, fun candle longV shortV -> this.OnProcess(candle, longV, shortV))
.Start() |> ignore
- Al trabajar con gráficos, es importante tener en cuenta que al ejecutar la estrategia fuera de Designer, el objeto de gráfico puede estar ausente.
// ------------- Configurar gráfico -------------
let area = this.CreateChartArea()
// area puede ser null si no hay GUI (por ejemplo, Runner o app de consola)
if not (isNull area) then
// Dibujar velas
this.DrawCandles(area, subscription) |> ignore
// Dibujar indicadores
this.DrawIndicator(area, shortSma, System.Drawing.Color.Coral) |> ignore
this.DrawIndicator(area, longSma) |> ignore
// Dibujar operaciones propias
this.DrawOwnTrades(area) |> ignore
- Inicie la protección de posición mediante StartProtection si la lógica de la estrategia lo requiere:
this.StartProtection(this.TakeValue, this.StopValue)
- La propia lógica de la estrategia, implementada en el método OnProcess. El método es llamado por la suscripción creada en el paso 1:
member private this.OnProcess
(
candle: ICandleMessage,
longValue: decimal,
shortValue: decimal
) =
// Registrar información de la vela
this.LogInfo(
LocalizedStrings.SmaNewCandleLog,
candle.OpenTime,
candle.OpenPrice,
candle.HighPrice,
candle.LowPrice,
candle.ClosePrice,
candle.TotalVolume,
candle.SecurityId
)
// Omitir si la vela no está finalizada
if candle.State <> CandleStates.Finished then
()
else
// Determinar si la SMA corta es menor que la SMA larga
let shortLess = shortValue < longValue
match isShortLessThenLong with
| None ->
// Primera vez: solo recordar la relación actual
isShortLessThenLong <- Some shortLess
| Some prevValue when prevValue <> shortLess ->
// Se produjo un cruce
// Si short < long, significa vender; en caso contrario, comprar
let direction =
if shortLess then
Sides.Sell
else
Sides.Buy
// Calcular volumen para abrir una nueva posición o revertir
// Si no hay posición, usar Volume; en caso contrario, duplicar
// el mínimo entre el tamaño absoluto de la posición y Volume
let vol =
if this.Position = 0m then
this.Volume
else
(abs this.Position |> min this.Volume) * 2m
// Obtener paso de precio para el precio límite
let priceStep =
let step = this.GetSecurity().PriceStep
if step.HasValue then step.Value else 1m
// Establecer el precio de la orden límite ligeramente por encima/debajo del cierre actual
let limitPrice =
match direction with
| Sides.Buy -> candle.ClosePrice + priceStep
| Sides.Sell -> candle.ClosePrice - priceStep
| _ -> candle.ClosePrice // no debería ocurrir
// Enviar orden limitada
match direction with
| Sides.Buy -> this.BuyLimit(limitPrice, vol) |> ignore
| Sides.Sell -> this.SellLimit(limitPrice, vol) |> ignore
| _ -> ()
// Actualizar la bandera de seguimiento
isShortLessThenLong <- Some shortLess
| _ ->
// No hacer nada si no hay cruce
()