Ensinar como transplantar uma estratégia de linguagem (avançado)

Criado em: 2019-11-13 09:15:56, atualizado em: 2024-12-15 16:02:09
comments   0
hits   2306

Ensinar como transplantar uma estratégia de linguagem (avançado)

Ensinar como transplantar uma estratégia de linguagem (avançado)

Artigo anteriorEnsine como escrever estratégias passo a passo - transplante uma estratégia de minha linguagemNo artigo, conduzimos um teste de transplante em uma estratégia simples de linguagem Mai. Se for uma estratégia de linguagem Mai um pouco mais complexa, como transplantá-la para a linguagem JavaScript? Quais são as técnicas para isso?

Vejamos primeiro a estratégia a ser transplantada desta vez:

(*backtest
start: 2019-05-01 00:00:00
end: 2019-11-12 00:00:00
period: 1d
exchanges: [{"eid":"Futures_OKCoin","currency":"BTC_USD"}]
args: [["SlideTick",10,126961],["ContractType","quarter",126961]]
*)

N1:=10;
N2:=21;
AP:=(HIGH+LOW+CLOSE)/3;
ESA:=EMA(AP,N1);
D:=EMA(ABS(AP-ESA),N1);
CI:=(AP-ESA)/(0.015*D);
TCI:=EMA(CI,N2);
WT1:TCI;
WT2:SMA(WT1,4,1);
AA:=CROSS(WT1,WT2);
BB:=CROSSDOWN(WT1,WT2);
REF(AA,1),BPK;
REF(BB,1),SPK;

O início desta estratégia linguística(*backtest...*)É o código de configuração da configuração do backtest. Para facilitar a comparação, uma configuração unificada do backtest é definida. Essa estratégia também é uma estratégia encontrada aleatoriamente, não é muito complicada (em relação à do artigo anterior) e é uma estratégia mais representativa. Para transplantar uma estratégia de linguagem Mai, você deve primeiro ler o conteúdo da estratégia. O código da estratégia de linguagem Mai é relativamente conciso, e basicamente você pode ter um certo entendimento da estratégia geral. Esta estratégia usa várias funções indicadoras.EMASMA

Construa uma roda primeiro

  • EMA Esta função de indicador tem uma função de biblioteca de indicadores pronta para uso ao escrever estratégias em linguagem JavaScript na plataforma FMZ. Agora mesmo:TA.MA

  • SMA O que precisamos fazer éSMADescobrimos que a função do indicador SMA não é suportada na biblioteca TA da FMZ. Também há diferenças entre o indicador SMA na biblioteca talib e o da linguagem Mai: Ensinar como transplantar uma estratégia de linguagem (avançado) Como você pode ver, além do parâmetro de período, há também um parâmetro de peso na parte de parâmetros.

A descrição da função do indicador SMA na biblioteca talib na documentação da API FMZ é: Ensinar como transplantar uma estratégia de linguagem (avançado)

visíveltalib.SMAÉ um indicador simples de média móvel. Desta forma, você só pode implementar um por si mesmoSMAComo um desenvolvedor que escreve estratégias usando a linguagem Javascript, essa também é uma das habilidades necessárias. Afinal, se não há rodas prontas, o carro ainda precisa ser dirigido, então é só construir uma.

Para ser honesto, não há muita pesquisa sobre indicadores e coisas do tipo. Geralmente, se não entendo algo, eu apenas pesquiso e procuro informações. Para SMA, encontramos o seguinte: Ensinar como transplantar uma estratégia de linguagem (avançado)

Sinto que o processo de algoritmo descrito aqui é bastante confiável. Vamos implementá-lo:

  function SMA (arr, n, m) {
      var sma = []
      var currSMA = null
      for (var i = 0; i < arr.length; i++) {
          if (arr[i] && !isNaN(arr[i])) {
              if (!currSMA) {
                  currSMA = arr[i]
                  sma.push(currSMA)
                  continue
              }  

              // [M*C2+(N-M)*S1]/N
              currSMA = (m * arr[i] + (n - m) * currSMA) / n
              sma.push(currSMA)
          } else {
              sma.push(NaN)
          }
      }  

      return sma
  }

Escrevendo a Seção de Preenchimento

Uso da estrutura de políticasEnsine como escrever estratégias passo a passo - transplante uma estratégia de minha linguagemA mesma estrutura no artigo preenche principalmente duas partes: Ensinar como transplantar uma estratégia de linguagem (avançado)

Primeiro, faça o processamento de dados de mercado e o cálculo de indicadores. Ensinar como transplantar uma estratégia de linguagem (avançado)

Abordaremos as funções da língua Mai uma por uma:

  • 1、AP:=(HIGH+LOW+CLOSE)/3;

Esta frase pode ser entendida como a adição do preço mais alto, do preço mais baixo e do preço de fechamento de cada BAR nos dados da linha K e a divisão por 3, calculando a média e, em seguida, armazenando-a como uma matriz, correspondendo a cada BAR um por um. Isso pode ser tratado assim:

  function CalcAP (r) {   // AP:=(HIGH+LOW+CLOSE)/3;
      var arrAP = []      // 声明一个空数组

      for (var i = 0; i < r.length; i++) {      // r为传入的K线数据,是一个数组,用for遍历这个数组
          v = (r[i].High + r[i].Low + r[i].Close) / 3      // 计算 平均值
          arrAP.push(v)                                    // 添加在 arrAP数组的尾部,arrAP是空的时候尾部就是第一个。
      }  

      return arrAP     // 返回 这个平均值数组,即麦语言中计算的 AP 
  }

Basta chamar esta função na função OnTick do loop principal, por exemplo:

  // 计算指标
  // AP
  var ap = CalcAP(records)
  • 2. Após a conclusão do cálculo do AP, continue a calcularESA:=EMA(AP,N1);:

Aqui precisamos usar os dados AP calculados na etapa anterior para calcular ESA. Na verdade, este ESA é a “média móvel exponencial” do AP, ou seja, o indicador EMA, então usamos AP como os dados e N1 como o parâmetro do indicador EMA para calcular o indicador EMA. .

  function CalcESA (ap, n1) {   // ESA:=EMA(AP,N1);
      if (ap.length <= n1) {    // 如果AP的长度小于指标参数,是无法计算出有效数据的,这个时候让函数返回false。
          return false
      }  

      return TA.EMA(ap, n1)
  }
  • 3、D:=EMA(ABS(AP-ESA),N1);

Use o calculadoAPESACalcular dadosD。 Você pode dar uma olhada nos comentários do código aqui para obter algumas dicas sobre cálculos de indicadores.

  function CalcD (ap, esa, n1) {    // D:=EMA(ABS(AP-ESA),N1);
      var arrABS_APminusESA = []
      if (ap.length != esa.length) {
          throw "ap.length != esa.length"
      }  

      for (var i = 0; i < ap.length; i++) {
          // 计算指标数值时,必须判断一下数据的有效性,因为前几次EMA计算可能数组中的开始部分的数据是NaN,或者null
          // 所以必须判断,参与计算的数据都是有效数值才能进行,如果有任何无效数值,就用NaN向arrABS_APminusESA填充
          // 这样计算得到的数据,每个位置和之前的数据都是一一对应的,不会错位。
          if (ap[i] && esa[i] && !isNaN(ap[i]) && !isNaN(esa[i])) {
              v = Math.abs(ap[i] - esa[i])     // 根据ABS(AP-ESA) , 具体计算数值,然后放入arrABS_APminusESA数组
              arrABS_APminusESA.push(v)
          } else {
              arrABS_APminusESA.push(NaN)
          }
      }  

      if (arrABS_APminusESA.length <= n1) {
          return false
      }  

      return TA.EMA(arrABS_APminusESA, n1)    // 计算数组arrABS_APminusESA的EMA指标,得到数据D(数组结构)
  }
  • 4、CI:=(AP-ESA)/(0.015*D); Este método de cálculo é semelhante ao passo 1, e o código é liberado diretamente.
  function CalcCI (ap, esa, d) {    // CI:=(AP-ESA)/(0.015*D);
      var arrCI = []
      if (ap.length != esa.length || ap.length != d.length) {
          throw "ap.length != esa.length || ap.length != d.length"
      }
      for (var i = 0; i < ap.length; i++) {
          if (ap[i] && esa[i] && d[i] && !isNaN(ap[i]) && !isNaN(esa[i]) && !isNaN(d[i])) {
              v = (ap[i] - esa[i]) / (0.015 * d[i])
              arrCI.push(v)
          } else {
              arrCI.push(NaN)
          }
      }  

      if (arrCI.length == 0) {
          return false
      }  

      return arrCI
  }
  • TCI:=EMA(CI,N2); Basta calcular o indicador EMA para a matriz CI.
  function CalcTCI (ci, n2) {   // TCI:=EMA(CI,N2);
      if (ci.length <= n2) {
          return false
      }  

      return TA.EMA(ci, n2)
  }
  • WT2:SMA(WT1,4,1);

Este último passo usa a roda que construímos anteriormente.SMAfunção.

  function CalcWT2 (wt1) {    // WT2:SMA(WT1,4,1);
      if (wt1.length <= 4) {
          return false 
      }  

      return SMA(wt1, 4, 1)   // 使用我们自己实现的SMA函数计算出wt1的SMA指标。
  }

O transplante de sinais de negociação é muito simples.

AA:=CROSS(WT1,WT2);
BB:=CROSSDOWN(WT1,WT2);
REF(AA,1),BPK;
REF(BB,1),SPK;

Lendo essas sentenças do código de idioma Mai, podemos saber que o julgamento golden cross e dead cross das duas linhas indicadoras WT1 e WT2 são usados ​​como condições para abrir uma posição. Deve-se notar que o sinal de cross anterior é usado. Teste diretamente a estratégia com a linguagem do microfone e observamos: Ensinar como transplantar uma estratégia de linguagem (avançado)

Por meio da operação real da Estratégia da Linguagem Mai, pode-se observar que, quando um sinal é detectado no ponto de abertura, ele está, na verdade, detectando se a posição 2 BARs antes do ponto de abertura é uma cruz dourada. Pode ser visto claramente na figura acima: Ensinar como transplantar uma estratégia de linguagem (avançado) Ensinar como transplantar uma estratégia de linguagem (avançado)

O código de preenchimento da parte de detecção de sinal pode ser escrito como:

if ((_State == IDLE || _State == SHORT) && wt1[wt1.length - 4] < wt2[wt2.length - 4] && wt1[wt1.length - 3] > wt2[wt2.length - 3]) {
    if (_State == IDLE) {
        _State = OPENLONG
        Log("OPENLONG")    // 测试
    }
    if (_State == SHORT) {
        _State = COVERSHORT
        Log("COVERSHORT")  // 测试
    }
    isOK = false  
}

if ((_State == IDLE || _State == LONG) && wt1[wt1.length - 4] > wt2[wt2.length - 4] && wt1[wt1.length - 3] < wt2[wt2.length - 3]) {
    if (_State == IDLE) {
        _State = OPENSHORT
        Log("OPENSHORT")  // 测试
    }
    if (_State == LONG) {
        _State = COVERLONG
        Log("COVERLONG")  // 测试
    }
    isOK = false   
}

Aqui podemos pensar por que as instruções SPK e BPK da linguagem Mai podem ser implementadas com o código acima.

Testes retrospectivos

Configuração de backtest: Ensinar como transplantar uma estratégia de linguagem (avançado)

Versão de backtest do idioma Mai: Ensinar como transplantar uma estratégia de linguagem (avançado)

Backtest da versão JavaScript: Ensinar como transplantar uma estratégia de linguagem (avançado) Ensinar como transplantar uma estratégia de linguagem (avançado)

O código no início da função OnTick é usado para acelerar o processo de backtesting e executar a estratégia com base no modelo de preço de fechamento. Se você estiver interessado, pode analisá-lo em detalhes.

function OnTick(){
    // 驱动策略的行情处理部分
    var records = _C(exchange.GetRecords)
    if (records[records.length - 1].Time == preTime) {
        if (isOK) {
            Sleep(500)
            return 
        }
    } else {
        preTime = records[records.length - 1].Time
    }
    ...
    ..
    .

Código de estratégia de ensino completo: https://www.fmz.com/strategy/174457

Obrigado pela leitura