Documentação

Aplicações

Última atualização em 10. 11. 2021 por Mark Fric

Chamando Python da SQ (Java) - Introdução

Python é uma linguagem muito popular, especialmente na comunidade científica e quantitativa, graças às suas extensas bibliotecas numéricas e estatísticas.
Neste artigo, mostraremos algumas maneiras de chamar o Python a partir do Snippet Java do StrategyQuant.

Script Python

Usaremos um script Python muito simples que será definido no arquivo sqpython.py

print("Hello from Python!")

Se você o chamar pelo interpretador Python, ele retornará a mensagem "Hello from Python!".

$ python sqpython.py
Olá do Python!

Neste exemplo, chamaremos esse script Python a partir do snippet CustomAnalysis - ele chamará o script e armazenará sua saída em um valor especial.

Para ver a saída do script Python no SQ, criaremos uma coluna especial do banco de dados que mostrará esse valor. Esse exemplo é uma variação do exemplo de análise personalizada: Exemplo - análise personalizada por estratégia.

Após a Passo 6 No exemplo anterior, criaremos uma coluna personalizada do banco de dados que lerá e exibirá o valor da string armazenada na variável de resultado da estratégia (isso será feito pelo snippet Custom Analysis depois de chamar o script Python).

Todo o código do snippet da nova coluna do banco de dados é:

pacote SQ.Columns.Databanks;

import com.strategyquant.tradinglib.DatabankColumn;
import com.strategyquant.tradinglib.ResultsGroup;;
import com.strategyquant.tradinglib.ValueTypes;

classe pública TestPythonOutput extends DatabankColumn {

    public TestPythonOutput() {
        super("TestPythonOutput", DatabankColumn.Text, ValueTypes.Maximize, 0, 0, 100);
    }
  
  //------------------------------------------------------------------------
    
    @Override
    public String getValue(ResultsGroup rg, String resultKey, byte direction, byte plType, byte sampleType) throws Exception {
        String value = rg.specialValues().getString("PythonOutput", NOT_AVAILABLE);
        
        return value;
    }
}

 

Se você compilar esse snippet, reiniciar o SQ e adicionar essa nova coluna ao seu banco de dados, verá a string "N/A" nessa coluna - isso ocorre porque ainda não chamamos nenhum script Python para preenchê-la:

Coluna do banco de dados Saída Python NA

Nosso objetivo com o Python é preencher essa coluna com o valor retornado pelo Python.

 

Chamada de scripts Python a partir de Java usando Jython

Proalvez a maneira mais fácil de chamar o Python a partir do SQ seja usando Jython. O Jython é uma implementação Java do Python que é executada na JVM, o que significa que é uma biblioteca Java pura, sem dependências externas.

Você pode baixá-lo (a versão autônoma JAR) em https://www.jython.org/download

e coloque-o em seu {Instalação do SQ pasta.

Essa é uma pasta na qual as bibliotecas JAR personalizadas podem ser colocadas e são localizadas e carregadas pelo SQ durante a inicialização.

Quando o jython-standalone-2.7.2.jar estiver lá e você reiniciar o SQ, poderá usar a funcionalidade do Jython no SQ.

Agora, criaremos um novo snippet de análise personalizada chamado CAJythonScript com o seguinte código:

pacote SQ.CustomAnalysis;

import java.io.FileReader;
java.io.StringWriter;
import java.util.List;

import javax.script.*;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.strategyquant.lib.app.MainApp;
import com.strategyquant.tradinglib.CustomAnalysisMethod;
import com.strategyquant.tradinglib.Result;
import com.strategyquant.tradinglib.ResultsGroup;

classe pública CAJythonScript estende CustomAnalysisMethod {
    public static final Logger Log = LoggerFactory.getLogger(CAJythonScript.class);
    
    //------------------------------------------------------------------------
    //------------------------------------------------------------------------
    //------------------------------------------------------------------------
    
    public CAJythonScript() {
        super("CAJythonScript", TYPE_FILTER_STRATEGY);
    }
    
    //------------------------------------------------------------------------
    
    @Override
    public boolean filterStrategy(String project, String task, String databankName, ResultsGroup rg) throws Exception {
        Log.info("Chamando o script Jython");

        StringWriter writer = new StringWriter();
        ScriptContext context = new SimpleScriptContext();
        context.setWriter(writer);

        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("jython");
        String pythonScript = MainApp.getDataPath()+"sqpython.py";
        Log.info("Caminho do script Jython: "+pythonScript);
        engine.eval(new FileReader(pythonScript), context);

        String result = writer.toString().trim();

        Log.info("Saída do script Jython: "+resultado);

        rg.specialValues().set("PythonOutput", result);

        return true;
    }
}

O código é bastante simples, ele usa a funcionalidade do mecanismo de script incorporado do Java e escolheremos o mecanismo "jython" pelo nome.

Então, chamaremos apenas motor.eval() e passe a ele o caminho para o nosso arquivo de script Python como parâmetro.

Esse snippet executa o script Python, lê sua saída e salva essa saída na chave "PythonOutput" no mapa de valores personalizados em nossa estratégia (ResultGroup).

Quando você compilar, reinicie o SQ e execute nosso novo snippet de análise personalizada.

Você pode executar um snippet de análise personalizado como parte do processo do Builder/Retester ou como uma tarefa personalizada em um projeto personalizado. Em nosso exemplo, simplesmente o executaremos no Retester:

Trecho de python de análise personalizada

Observe que o script de análise personalizada é chamado para cada estratégia após o reteste, portanto, certifique-se de pressionar Start (Iniciar) para executar o reteste.

Você deve ver que a coluna TestPythonOutput do nosso banco de dados de controle agora contém o texto "Hello from Python!" - que é a saída do nosso script Python:

Saída python da coluna do banco de dados

Isso significa que o script Python foi chamado corretamente e retornou o valor que agora é exibido em nossa coluna personalizada do banco de dados.

Vantagens do Jython:

  • Chamada relativamente rápida - nenhum processo externo precisa ser chamado, tudo é tratado internamente na JVM
  • Simples de usar - basta adicionar uma biblioteca .JAR ao SQ

Desvantagens:

  • O Jython é a implementação do Python para Java e pode não conter todos os mesmos subpacotes do Python nativo
  • Ele não está mais sendo desenvolvido ativamente, portanto, está disponível apenas para uma versão mais antiga do Python

O Jython é adequado para scripts simples, mas se você precisar de todo o poder do Python, com todas as suas bibliotecas compatíveis, deverá usar uma maneira diferente.

 

Chamando o Python a partir do Java, invocando uma instalação externa do Python

Como o título sugere, esse método invocará um processo nativo do sistema operacional para iniciar o python e executar nosso script simples.

Isso significa que você deve ter o Python instalado em seu computador. A instalação do Python está além do escopo deste artigo, mas você pode seguir a documentação no site https://www.python.org/ site.

Você deve instalar o Python em seu computador para que possa executar scripts normalmente chamando

$ python myscript.py

na linha de comando.

Criaremos um novo snippet de análise personalizada chamado CAPythonScript com o seguinte código:

pacote SQ.CustomAnalysis;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
Importar java.util.List;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.strategyquant.lib.app.MainApp;
import com.strategyquant.tradinglib.CustomAnalysisMethod;
import com.strategyquant.tradinglib.Result;
import com.strategyquant.tradinglib.ResultsGroup;

public class CAPythonScript extends CustomAnalysisMethod {
    public static final Logger Log = LoggerFactory.getLogger(CAPythonScript.class);
    
    //------------------------------------------------------------------------
    //------------------------------------------------------------------------
    //------------------------------------------------------------------------
    
    public CAPythonScript() {
        super("CAPythonScript", TYPE_FILTER_STRATEGY);
    }
    
    //------------------------------------------------------------------------
    
    @Override
    public boolean filterStrategy(String project, String task, String databankName, ResultsGroup rg) throws Exception {
    
        String pythonScript = MainApp.getDataPath()+"sqpython.py";
        
        Log.info("Chamando o script Python: "+pythonScript);
        
        ProcessBuilder processBuilder = new ProcessBuilder("py", pythonScript);
        processBuilder.redirectErrorStream(true);

        Process process = processBuilder.start();
        String result = readProcessOutput(process.getInputStream());

        int exitCode = process.waitFor();
        
        Log.info("Saída do script Python: "+resultado);

        rg.specialValues().set("PythonOutput", result);
        
        return true;
    }

    //------------------------------------------------------------------------

    private String readProcessOutput(InputStream stream) {
        BufferedReader reader = new BufferedReader(new InputStreamReader(stream));

        StringBuilder stringBuilder = new StringBuilder();

        try {
        String line;
        while ((line = reader.readLine()) != null) {
            stringBuilder.append(line);
        }

        stream.close();

        } catch (IOException ex) {
        Log.error("Error: ", ex);
        }
        
        return stringBuilder.toString();
    }
}

Nesse trecho, a chamada do script externo é feita usando o ProcessBuilder, mas, mais uma vez, é simples de seguir.

Você precisa criar um ProcessBuilder com os parâmetros fornecidos e, em seguida, executá-lo chamando o comando .start() e coletar sua saída.

Isso chamará o Python instalado externamente, de modo que ele poderá conter quaisquer bibliotecas e módulos que você possa usar em seu Python padrão.

 

Chamando o Python por HTTP

Os dois exemplos anteriores tratavam da execução direta de scripts Python, pois você poderia fazer isso na linha de comando.

Uma maneira diferente de chamar o Python a partir do SQ seria usar um protocolo HTTP como camada de abstração entre as duas linguagens diferentes.

Por padrão, o Python vem com um simples servidor HTTP integrado que pode ser usado para compartilhar conteúdo ou arquivos por HTTP:

python -m http.server 9000

 

Se você for para http://localhost:9000você verá o conteúdo listado para o diretório em que lançamos o comando anterior.

Você pode usar estruturas da Web populares, como Frasco ou Django para criar pontos de extremidade da Web em Python.
Depois de ter os pontos de extremidade da Web no Python, você poderá chamá-los do SQ usando uma das bibliotecas HTTP do Java.

Criaremos um novo snippet de análise personalizado chamado CAPythonHttp que chama o Python via HTTP:

pacote SQ.CustomAnalysis;

import com.strategyquant.lib.SQUtils;
import com.strategyquant.lib.app.MainApp;
import com.strategyquant.tradinglib.CustomAnalysisMethod;
import com.strategyquant.tradinglib.ResultsGroup;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Importar javax.script.ScriptContext;
Importar javax.script.ScriptEngine;
Importar javax.script.ScriptEngineManager;
Importar javax.script.SimpleScriptContext;
importar java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.List;

classe pública CAPythonHttp extends CustomAnalysisMethod {
    public static final Logger Log = LoggerFactory.getLogger(CAPythonHttp.class);

    //------------------------------------------------------------------------
    //------------------------------------------------------------------------
    //------------------------------------------------------------------------

    public CAPythonHttp() {
        super("CAJythonScript", TYPE_FILTER_STRATEGY);
    }
    
    //------------------------------------------------------------------------
    
    @Override
    public boolean filterStrategy(String project, String task, String databankName, ResultsGroup rg) throws Exception {
        HttpURLConnection connection = null;

        try {
            Log.info("Chamando o Python via HTTP");

            URL url = novo URL("http://localhost:9000");
            conexão = (HttpURLConnection)url.openConnection();
            conexão.setRequestMethod("GET");
            connection.setRequestProperty("Content-Type", "text/html");
            connection.setRequestProperty("Content-Language", "en-US");

            conexão.setUseCaches(false);
            connection.setDoOutput(true);

            // obter resposta
            InputStream is = connection.getInputStream();
            BufferedReader rd = new BufferedReader(new InputStreamReader(is));
            StringBuilder response = new StringBuilder(); // ou StringBuffer se não for Java 5+
            Linha String;
            while((line = rd.readLine()) != null) {
                response.append(line);
                response.append('\r');
            }
            rd.close();

            String result = response.toString();

            // remover tags HTML da resposta
            result = result.replaceAll("\\", "");

            Log.info("Saída do script PythonHTTP: "+resultado);

            // armazenaremos apenas os primeiros 10 caracteres do resultado
            rg.specialValues().set("PythonOutput", result.substring(0, 10));

        } catch (Exception e) {
            throw new Exception("Erro de solicitação: GET, " + e.getMessage());
        } finally {
            if(connection != null) {
                connection.disconnect();
            }
        }

        return true;
    }
}

Novamente, deve ser fácil de seguir. O script criará uma solicitação HTTP para um determinado URL e lerá a resposta.

Neste exemplo, a resposta seria um longo documento HTML contendo a listagem de diretórios, portanto, removeremos as tags HTML e a encurtaremos para 10 caracteres.

Você deve ver a saída como:

Coluna do banco de dados Python http

 

Passagem de parâmetros

É aqui que as coisas ficam interessantes e mais complicadas. Chamar scripts Python do SQ sem passar nenhum parâmetro para eles tem um uso muito limitado.

Normalmente, você deseja passar alguns (ou muitos) parâmetros do SQ para o Python - nome da estratégia, lista de negociações, resultados de testes etc. - para que possa trabalhar com eles no Python e possivelmente retornar algo.

A maneira de passar esses parâmetros e obter resultados do Python depende do método de invocação do Python que você usa.

No caso de uma chamada HTTP, a situação é simples.

Você pode transmitir até mesmo uma quantidade muito grande de dados entre o SQ e o Python usando solicitação/resposta HTTP padrão.

Você pode enviar dados para o endpoint da Web do Python como parâmetros GET padrão ou dados POST.

No caso de uma chamada de script Python externo (usando Jython ou invocação Python)

Se os dados passados forem pequenos (por exemplo, apenas o nome da estratégia), você poderá passá-los como argumentos do script.

Ao enviar dados maiores, uma possibilidade é salvar os dados em um arquivo temporário em qualquer formato desejado no SQ e, em seguida, carregar os dados desse arquivo temporário no Python. Você pode usar a mesma maneira para enviar dados maiores do Python para o SQ.

 

Este artigo foi útil? O artigo foi útil O artigo não foi útil

Assine
Notificação de
2 Comentários
Mais antigo
Novidades Mais Votados
Feedbacks em linha
Ver todos os comentários
Comerciante de abelhas
10. 11. 2021 1:24 pm

Obrigado

Emmanuel
13. 1. 2022 1:36 pm

obrigado

Postos relacionados