Exemplo de estratégia F#

A criação de uma estratégia a partir de código-fonte será demonstrada usando o exemplo da estratégia SMA, semelhante ao exemplo da estratégia SMA montada a partir de cubos na secção Criar algoritmo a partir de cubos.

Esta secção não descreve construções da linguagem F# (nem a Strategy, que é usada como base para criar estratégias), mas menciona funcionalidades específicas para trabalhar com código no Designer.

Tip

As estratégias criadas no Designer são compatíveis com estratégias criadas na API, devido à utilização da classe base comum Strategy. Isto torna a execução dessas estratégias fora do Designer significativamente mais simples do que executar esquemas.

  1. Os parâmetros da estratégia são criados usando uma abordagem especial:
// --- Parâmetros da estratégia: CandleType, Long, Short, TakeValue, StopValue ---

// Parâmetro para o tipo de candle
let candleTypeParam =
	this.Param<DataType>(nameof(this.CandleType), DataType.TimeFrame(TimeSpan.FromMinutes 1.0))
		.SetDisplay("Tipo de candle", "Tipo de candle para cálculo da estratégia.", "Configurações gerais")

// Parâmetro para a SMA longa
let longParam =
	this.Param<int>(nameof(this.Long), 80)

// Parâmetro para a SMA curta
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))

// Indicador para acompanhar o cruzamento das SMA
let mutable isShortLessThenLong : bool option = None

// --------------------- Propriedades públicas ---------------------

/// <summary>O tipo de candle usado pela estratégia.</summary>
member this.CandleType
	with get () = candleTypeParam.Value
	and set value = candleTypeParam.Value <- value

/// <summary>O período do indicador SMA longo.</summary>
member this.Long
	with get () = longParam.Value
	and set value = longParam.Value <- value

/// <summary>O período do indicador SMA curto.</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

Ao usar a classe StrategyParam, a abordagem de guardar e restaurar definições é aplicada automaticamente.

  1. Ao criar indicadores e subscrever dados de mercado, é necessário ligá-los para que os dados recebidos da subscrição possam atualizar os valores dos indicadores:
// ---------- Criar indicadores ----------
let longSma = SMA()
longSma.Length <- this.Long

let shortSma = SMA()
shortSma.Length <- this.Short
// ---------------------------------------

// ------ Subscrever o fluxo de candles e ligar indicadores ------
let subscription = this.SubscribeCandles(this.CandleType)

// Ligar os nossos indicadores à subscrição e atribuir a função de processamento
subscription
	.Bind(longSma, shortSma, fun candle longV shortV -> this.OnProcess(candle, longV, shortV))
	.Start() |> ignore
  1. Ao trabalhar com gráficos, é importante considerar que, ao executar a estratégia fora do Designer, o objeto do gráfico pode não existir.
// ------------- Configurar gráfico -------------
let area = this.CreateChartArea()

// area pode ser null caso não exista GUI (por exemplo, Runner ou aplicação de consola)
if not (isNull area) then
	// Desenhar candles
	this.DrawCandles(area, subscription) |> ignore

	// Desenhar indicadores
	this.DrawIndicator(area, shortSma, System.Drawing.Color.Coral) |> ignore
	this.DrawIndicator(area, longSma) |> ignore

	// Desenhar negócios próprios
	this.DrawOwnTrades(area) |> ignore
  1. Inicie a proteção da posição através de StartProtection, se exigido pela lógica da estratégia:
this.StartProtection(this.TakeValue, this.StopValue)
  1. A lógica da estratégia propriamente dita, implementada no método OnProcess. O método é chamado pela subscrição criada no passo 1:
member private this.OnProcess
	(
		candle: ICandleMessage,
		longValue: decimal,
		shortValue: decimal
	) =
	// Registar informações da candle
	this.LogInfo(
		LocalizedStrings.SmaNewCandleLog,
		candle.OpenTime,
		candle.OpenPrice,
		candle.HighPrice,
		candle.LowPrice,
		candle.ClosePrice,
		candle.TotalVolume,
		candle.SecurityId
	)

	// Ignorar se a candle não estiver finalizada
	if candle.State <> CandleStates.Finished then
		()
	else
		// Determinar se a SMA curta é menor do que a SMA longa
		let shortLess = shortValue < longValue

		match isShortLessThenLong with
		| None ->
			// Primeira vez: apenas memorizar a relação atual
			isShortLessThenLong <- Some shortLess
		| Some prevValue when prevValue <> shortLess ->
			// Ocorreu um cruzamento
			// Se short < long, isso significa Sell; caso contrário, Buy
			let direction =
				if shortLess then
					Sides.Sell
				else
					Sides.Buy

			// Calcular o volume para abrir uma nova posição ou inverter
			// Se não existir posição, usar Volume; caso contrário, duplicar
			// o mínimo entre o tamanho absoluto da posição e Volume
			let vol =
				if this.Position = 0m then
					this.Volume
				else
					(abs this.Position |> min this.Volume) * 2m

			// Obter o passo de preço para o preço limite
			let priceStep =
				let step = this.GetSecurity().PriceStep
				if step.HasValue then step.Value else 1m

			// Definir o preço da ordem limite ligeiramente acima/abaixo do preço de fecho atual
			let limitPrice =
				match direction with
				| Sides.Buy  -> candle.ClosePrice + priceStep
				| Sides.Sell -> candle.ClosePrice - priceStep
				| _          -> candle.ClosePrice // não deverá ocorrer

			// Enviar ordem limite
			match direction with
			| Sides.Buy  -> this.BuyLimit(limitPrice, vol) |> ignore
			| Sides.Sell -> this.SellLimit(limitPrice, vol) |> ignore
			| _          -> ()

			// Atualizar o indicador de acompanhamento
			isShortLessThenLong <- Some shortLess
		| _ ->
			// Não fazer nada se não houver cruzamento
			()