Estudos BOVESPA
Carregando, aguarde alguns segundos.

4 - Análise Técnica Intermediária (ATI)

Parte 1

Na Parte 1 da Análise Técnica Intermediária (ATI) abordaremos:

  • Tópico 1: Pesquisa de padrões de oscilação em uma série numérica ordenada sequencialmente por algum critério, por exemplo data, de modo que o menor índice, correspondente a 0 (zero) equivale à menor data, e o maior índice, correspondente a n-1 (tamanho da lista menos um), corresponde à maior data.
  • Tópico 2: Salvamento direto em disco dos arquivos zipados da Bovespa usando o Python, a partir do site da B3, sem a necessidade de acesso pelo programador ao site para download dos arquivos zipados de cotações dos pregões.
  • Tópico 3: Carregamento simultâneo de vários anos de cotações dos pregões da Bovespa e a pesquisa de padrões do dataframe com estas cotações.

4.1 - Tópico 1: Pesquisa de Padrões de Oscilação

A pesquisa de padrões de oscilação consiste na localização de conjuntos de sequências de valores em conjuntos maiores de valores, sejam numéricos ou de qualquer outro tipo que permita serem avaliados ao ao crescerem, diminuirem ou permanecerem iguais na sequência observada.

Consideremos um conjunto qualquer de valores numéricos inteiros.

{ 3 , 7 , 2, 16 , 18 , 15 , 13 , 12 , 18 , 16 , 20 , 30 , 40 , 12 , 13 }

Observando-se determinado valor na lista, e os valores anteriores este valor, qual a probabilidade do valor seguinte oscilar para cima, para baixo, ou permanecer igual?

Primeiro representaremos o conjunto acima com os símbolos seta para cima (↑), seta pra baixo (↓) e igual(=).

  • Sendo o valor do índice atual maior que o do índice anterior, usaremos o caracter da seta para cima { ↑ } para representar este valor.
  • Sendo o valor do índice atual menor que o do índice anterior, usaremos o caracter da seta para baixo { ↓ } para representar este valor.
  • Sendo o valor do índice atual igual ao do índice anterior, usaremos o caracter da igual { = } para representar este valor.
  • Como o primeiro índice não tem índice anterior, usaremos o caracter de interrogação (?) como sinal na primeira posição, com o primeiro índice sendo sendo descartado na pesquisa de padrões.

O conjunto numérico anterior fica assim representado:

{ ? , ↑ , ↓ , ↑ , ↑ , ↓ , ↓ , ↓ , ↑ , ↓ , ↑ , ↑ , ↑ , ↓ , ↑ }

Observe a correspondência entre os sinais e os valores numéricos.

{ 3 (?) , 7 (↑) , 2 (↓) , 16 (↑) , 18 (↑) , 15 (↓) , 13 (↓), 12 (↓), 18 (↑), 16 (↓), 20 (↑), 30 (↑), 40 (↑), 12 (↑), 13 (↑) }

Agora observe os dois últimos caracteres: : { ↓ , ↑ }.

Qual a probabilidade, baseado na averiguação dos sinais de oscilação, dos valores numéricos da lista, do próximo caracter ser { ↑ }, { ↓ } ou { = }?

A cada índice da lista de valores comparamos o valor do índice com o do índice anterior.

Após { ↓ , ↑ }, iniciadas nas posições marcadas com "*", contamos duas ocorrências de subida (com { ↑ } após { ↓ , ↑ }), uma ocorrência de descida { com { ↓ } após { ↓ , ↑ }) e nenhuma de permanência (com { = } após { ↓ , ↑ }).

{ ? , ↑ , *↓ , ↑ , ↑ , ↓ , ↓ , *↓ , ↑ , *↓ , ↑ , ↑ , ↑ , ↓ , ↑ }

Assim, teriamos 66% de chance do valor seguinte aos dois últimos ({ ↓ , ↑ }) de subir (↑), 33% de chance de descer (↓) e 0% de chance de permanecer (=), considerando o histórico da sequência.

No tópico 3 analisaremos padrões com uma sequência de vários anos de pregões para avaliar os percentuais de crescimentos, diminuições e igualdades de oscilações.

4.1.1 - Código Python

Agora, iniciaremos a codificação em Python dos recursos para pesquisa de padrões de oscilação em conjuntos com sequências de valores.

Declaramos variáveis contendo os sinais utilizados para as oscilações de subida, descida, permanência e indeterminação.

sobe  = "↑"
desce = "↓"
igual = "="
indet = "?"

As funções declaradas a seguir permitirão operações com os padrões de oscilação de listas numéricas.

4.1.2 - Função cieda_b3_sinais_oscilacao

A função cieda_b3_sinais_oscilacao retorna o texto convertido, com os sinais representativos das oscilações dos valores numéricos da série.

Por exemplo a lista [1, 3, 5, 2 , 6 , 3 , 2, 3] retornará o texto convertido "?↑↑↓↑↓↓↑".

Uma lista com n números permite n-t mapeamentos de sequências internas de pesquisa, onde t é o tamanho da sequência de pesquisa.

O primeiro índice (zero) não permite a avaliação da oscilação por não haver valor anterior de referência.

A função fica:

def cieda_b3_sinais_oscilacao(lista):
    i = 0
    caracs = ""
    for valor in lista:
        if i == 0:
            carac = "?"
            valor_anterior = 0.0
        else:
            if valor_anterior < valor:
                carac = "↑"
            elif  valor_anterior > valor:
                carac = "↓"
            else:
                carac = "="
            valor_anterior = valor
        caracs += carac
        i += 1
    return caracs

Agora vamos declarar uma lista do Python com os valores numéricos do conjunto declarado anteriormente.

dados = [3 , 7 , 2, 16 , 18 , 15 , 13 , 12 , 18 , 16 , 20 , 30 , 40 , 12 , 13]
print(dados)

Convertemos a lista de valores numéricos para o texto de sinais de oscilação usando os caracteres { ↑ , ↓ , = , ? }

representacao_dados = cieda_b3_sinais_oscilacao(dados)
print(representacao_dados)

4.1.3 - Função cieda_b3_contar_ocorrencias_sinais_oscilacao

A função cieda_b3_contar_ocorrencias_sinais_oscilacao retorna a contagem de ocorrências de uma pesquisa de padrão em uma lista de sinais de oscilação.

def cieda_b3_contar_ocorrencias_sinais_oscilacao(lista_oscilacoes,texto_pesquisa):
    sim = 0
    nao = 0
    tam = len(texto_pesquisa)
    while len(lista_oscilacoes) >= tam:
        lista_oscilacoes[:tam]
        achou = (texto_pesquisa == aux)
        if achou:
            sim += 1
        else:
            if aux[0] != "?": nao += 1
        lista_oscilacoes = lista_oscilacoes[1:]
    return sim, nao

Contamos as ocorrências do subconjunto { ↓ , ↑ }.

sim, nao = cieda_b3_contar_ocorrencias_sinais_oscilacao(representacao_dados,"↓↑")
print("sim:", sim, "não:", nao)

Usando o texto convertido temos as seguintes sequências para comparação com o texto de pesquisa nos índices indicados.

?↑↓↑↑↓↓↓↑↓↑↑↑↓↑

0

.1

..2

...3

....4

.....5

......6

.......7

........8

.........9

..........10

...........11

............12

.............13

..............14

$/des

A tabela a seguir apresenta as colunas:

  • Índice: posição do texto intermediário
  • Restante: sequência de caracteres para comparação com o texto pesquisado
  • Próximo: sequência intermediária a partir do índice do tamanho do texto pesquisado
  • Permite: indicação de que texto intermediário atende ao critério para comparação com o texto pesquisado.
  • Igual: indicação se o texto pesquisado é igual ao texto intermediário coomparado.

Sequência pesquisada (dois últimos caracteres): "↓↑".

Índice Restante próximo permite igual
0 ?↑↓↑↑↓↓↓↑↓↑↑↑↓↑ ?↑ não não
1 ↑↓↑↑↓↓↓↑↓↑↑↑↓↑ ↑↓ sim não
2 ↓↑↑↓↓↓↑↓↑↑↑↓↑ ↓↑ sim sim
3 ↑↑↓↓↓↑↓↑↑↑↓↑ ↑↑ sim não
4 ↑↓↓↓↑↓↑↑↑↓↑ ↑↓ sim não
5 ↓↓↓↑↓↑↑↑↓↑ ↓↓ sim não
6 ↓↓↑↓↑↑↑↓↑ ↓↓ sim não
7 ↓↑↓↑↑↑↓↑ ↓↑ sim sim
8 ↑↓↑↑↑↓↑ ↑↓ sim não
9 ↓↑↑↑↓↑ ↓↑ sim sim
10 ↑↑↑↓↑ ↑↑ sim não
11 ↑↑↓↑ ↑↑ sim não
12 ↑↓↑ ↑↓ sim não
13 ↓↑ ↓↑ sim sim
14 ↑ ↑ não -

4.1.4 - Função cieda_b3_estatisticas_oscilacoes

A função cieda_b3_estatisticas_oscilacoes retorna uma lista de listas, processando contagens e percentuais realizados na pesqwuisa do texto de busca sobre o texto de sinais de oscilação.

O texto pesquisado é comparado, índice a índice, com os textos intermediários presentes no texto convertido.

Os textos intermediários são os subconjuntos de textos em cada índice do texto convertido, com tamanho igual ao do texto pesquisado.

def cieda_b3_estatisticas_oscilacoes(lista_oscilacoes,texto_pesquisa):
    conta_sim   = 0
    conta_nao   = 0
    conta_sobe  = 0
    conta_desce = 0
    conta_igual = 0
    conta_indet = 0
    tam = len(texto_pesquisa)
    #print("tam:", tam, "tam2",  len(lista_oscilacoes))
    while len(lista_oscilacoes) > tam:
        aux = lista_oscilacoes[:tam]
        achou = (texto_pesquisa == aux)
        if achou:
            conta_sim += 1
            oscilacao = lista_oscilacoes[tam:tam+1]
            if oscilacao == "↑":
                conta_sobe += 1
            elif oscilacao == "↓":
                conta_desce += 1
            elif oscilacao == "=":
                conta_igual += 1
            else:
                conta_indet += 1
        else:
            if aux[0] != "?": conta_nao += 1
        lista_oscilacoes = lista_oscilacoes[1:]
    total = conta_sim + conta_nao
    cabecalho = [texto_pesquisa,total,conta_sim,conta_nao]
    conta_sobe_desce_igual_indet = [conta_sobe,conta_desce,conta_igual,conta_indet]
    #
    if conta_sim > 0:
        perc_sim = list(np.divide(conta_sobe_desce_igual_indet,conta_sim))
        perc_sim = [round(v * 10000) / 100 for v in perc_sim]
    else:
        perc_sim = [0,0,0,0]
    #
    if total > 0:
        perc_total = list(np.divide(conta_sobe_desce_igual_indet,total))
        perc_total = [round(v * 10000) / 100 for v in perc_total]
    else:
        perc_total = [0,0,0,0]
    #
    return [cabecalho,conta_sobe_desce_igual_indet,perc_total,perc_sim]

Contamos as ocorrências do subconjunto { ↓ , ↑ }.

res = cieda_b3_estatisticas_oscilacoes(representacao_dados,"↓↑")
print(res)

4.1.5 - Função cieda_b3_imprimir_estatisticas_oscilacoes

A função cieda_b3_imprimir_estatisticas_oscilacoes imprime as contagens e percentuais obtidos.

def cieda_b3_imprimir_estatisticas_oscilacoes(res):
    cabecalho                    = res[0]
    conta_sobe_desce_igual_indet = res[1]
    perc_total                   = res[2]
    perc_sim                     = res[3]
    print(
        "pesq: '{}' não: {} sim: {} ( ↑ {} {:.0f}% ↓ {} {:.0f}% = {} {:.0f}% ? {} {:.0f}% ) %tot: {} ( ↑ {:.0f}% ↓ {:.0f}% = {:.0f}% ? {:.0f}% )".format(
            cabecalho[0],
            cabecalho[3],
            cabecalho[2],
            conta_sobe_desce_igual_indet[0],
            perc_sim[0],
            conta_sobe_desce_igual_indet[1],
            perc_sim[1],
            conta_sobe_desce_igual_indet[2],
            perc_sim[2],
            conta_sobe_desce_igual_indet[3],
            perc_sim[3],
            cabecalho[1],
            perc_total[0],
            perc_total[1],
            perc_total[2],
            perc_total[3]))
cieda_b3_imprimir_estatisticas_oscilacoes(res)

Obtemos o dataframe de preços de fechamento (PREULT) do código de negociação "VALE3" do ano de 2022, de janeiro a setembro.

df_22_VALE3_PREULT = cieda_b3_df_cndp(df_a22,"PREULT","VALE3",22)

Imprimimos o objeto retornado para uma visualização básica do seu conteúdo.

print(df_22_VALE3_PREULT)

Verificamos o tipo do objeto retornado.

print(type(df_22_VALE3_PREULT))

4.1.6 - Função cieda_b3_montar_tabela_percentuais_por_ultimas_cotacoes

A função cieda_b3_montar_tabela_percentuais_por_ultimas_cotacoes retorna ...

def cieda_b3_montar_tabela_percentuais_por_ultimas_cotacoes(lista_osciladores,num_pregoes=6):
    i = 0
    t = len(lista_osciladores)
    continuar = True
    while (i < num_pregoes) and continuar:
        i += 1
        str_pesq = lista_osciladores[t-i:]
        res = cieda_b3_estatisticas_oscilacoes(
            lista_osciladores,
            str_pesq)
        conta_sim = res[0][2]
        if conta_sim > 0:
            cieda_b3_imprimir_estatisticas_oscilacoes(res)
        else:
            continuar = False

Apresentamos as pesquisas de padrão sobre ocorrências passadas para previsão contagem estatística de casos.

cieda_b3_montar_tabela_percentuais_por_ultimas_cotacoes(representacao_dados)

4.2 - Tópico 2: Importação direta de arquivos de cotações

Mostraremos agora como baixar diretamente, do site da B3, os arquivos zipados com as séries históricas das cotações.

Importe os pacotes como a seguir:

import os
from pathlib import Path
import requests as req

O pacote requests permite a conexão através do protocolo http com arquivos localizados na internet.

4.2.1 - função cieda_b3_req_get_response_cotacoes()

A função cieda_b3_req_get_response_cotacoes() retorna o objeto request para obter a sequência de bytes através da conexão com o arquivo da B3.

def cieda_b3_req_get_response_cotacoes(nome_arq_zip):
    url = "https://bvmf.bmfbovespa.com.br/InstDados/SerHist/"+nome_arq_zip
    headers = {
        'accept': '*/*',
        'accept-language': 'en-US,en;q=0.9,pt-BR;q=0.8,pt;q=0.7,es-MX;q=0.6,es;q=0.5',
        'content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
        'x-requested-with': 'XMLHttpRequest',
        'sec-ch-ua': '" Not;A Brand";v="99", "Google Chrome";v="97", "Chromium";v="97"',
        'sec-ch-ua-mobile': '?0',
        'sec-ch-ua-platform': '"macOS"',
        'sec-fetch-dest': 'empty',
        'sec-fetch-mode': 'cors',
        'sec-fetch-site': 'same-origin',
        'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko)'}
    return req.get(url, stream=True, headers = headers)

4.2.2 - função obter_nome_arq_zip_cotacoes()

A função obter_nome_arq_zip_cotacoes() retorna o nome do arquivo zipado de acordo com os argumentos ano, mes e dia.

def cieda_b3_obter_nome_arq_zip_cotacoes(ano,mes=None,dia=None):
    #
    if dia and mes:
        nome_arq_zip = "COTAHIST_D{:02d}{:02d}{}.ZIP".format(dia,mes,ano)
    elif mes:
        nome_arq_zip = "COTAHIST_M{:02d}{}.ZIP".format(mes,dia)
    else:
        nome_arq_zip = "COTAHIST_A{}.ZIP".format(ano)
    return nome_arq_zip

4.2.3 - função cieda_b3_salvar_req_get_cotacoes()

A função cieda_b3_salvar_req_get_cotacoes() salva em disco a sequência de bytes retornada.

def cieda_b3_salvar_req_get_cotacoes(
    ano,
    mes=None,
    dia=None,
    path=".",
    create_path=False, 
    overwrite=False,
    chunck_size=10*1024*1024):
    #
    if not os.path.exists(path):
        if create_path:
            os.makedirs(path)
        else:
            return -1
    #
    nome_arq_zip = cieda_b3_obter_nome_arq_zip_cotacoes(ano,mes,dia)
    #
    zip_path_file_name = os.path.join(path,nome_arq_zip)
    #
    if os.path.exists(zip_path_file_name) and not overwrite:
        return -2
    #
    get_response = cieda_b3_req_get_response_cotacoes(nome_arq_zip)
    #
    if get_response.status_code == 440:
        return -3
    f = open(zip_path_file_name, 'wb')
    for chunk in get_response.iter_content(chunk_size=chunck_size):
        if chunk: f.write(chunk)
    f.close()
    #
    return 1

O laço a seguir transporte do site e grava em disco as cotações dos arquivos dos anos de 1986 a 2022.

for ano in range(2023,2023):
    cieda_b3_salvar_req_get_cotacoes(ano,path="c:/bovespa/cotacoes")

4.3 - Tópico 3: Carregamento de vários anos de cotações

Agora podemos usar as cotações de vários anos para analisar um conjunto com bastante ocorrências de sinais de oscilação.

4.3.1 - função cieda_b3_df_ler_arqs_zip_cot()

A função cieda_b3_df_ler_arqs_zip_cot retorna um dataframe com todas as cotações dos anos inicial e final passados como argumentos na função.

def cieda_b3_df_ler_arqs_zip_cot(dfbvlay,zip_path,ano_inicial,ano_final):
    if (ano_final < ano_inicial) or (ano_inicial < 1986): return
    lista_completa = []
    for ano in range(ano_inicial,ano_final+1):
        nome_arq_cot_bv = os.path.join(zip_path,"COTAHIST_A{}.ZIP".format(ano))
        lista = cieda_b3_ler_arquivo_zip_cotacoes(dfbvlay,nome_arq_cot_bv)
        lista_completa.extend(lista)
    df = cieda_b3_df_cot_converter_lista(lista_completa)
    return cieda_b3_df_cot_ajustar(df)

Carregamos o dataframe com as cotações dos anos 2017 a 2022, num total de 6 anos de pregões da Bovespa.

if carregamento_leve:
    df_a17a22 = df_m1m9a22
else:
    df_a17a22 = cieda_b3_df_ler_arqs_zip_cot(dfbvlay,"c:/bovespa/cotacoes",2017,2022)

Imprimimos o dataset retornado e observamos que o dataframe contém mais de 6.600.000 linhas de cotação em 6 anos de pregões desde 2017 até 2022.

print(df_a17a22)

Utilizamos a função cieda_b3_df_cndp para obter a série com o preço de fechamento do código de negociação "VALE3".

df_17_22_VALE3_PREULT = cieda_b3_df_cndp(df_a17a22,"PREULT","VALE3")

Obtemos o texto com os sinais de representação de oscilação do dataframe.

res_6anos = cieda_b3_sinais_oscilacao(df_17_22_VALE3_PREULT)

Imprimimos o texto de sinais de oscilação.

print(res_6anos)

Observamos o conjunto de caracteres no rabo do texto de sinais de oscilação.

{ ... , ↑ , ↑ , ↓ , = , ↑ , ↓ , ↓ }

Vamos imprimir e analisar as estatísticas dos 10 últimos sinais de oscilação do texto convertido.

cieda_b3_montar_tabela_percentuais_por_ultimas_cotacoes(res_6anos,num_pregoes=10)
Arduino
Coautor
Betobyte
Autor
Autores
||| Áreas ||| Estatística ||| Python ||| Projetos ||| Dicas & Truques ||| Quantum ||| Estudos BOVESPA || Estudos BOVESPA || Aulas | Introdução (Apresentação do contexto) | ATB-1 (gráficos de linha, bollinger, histogramas) | ATB-2 (3D e correlações) | ATI-1 (Análise de padrões e consulta direta na B3) | ATI-2 (Banco de dados SQLite de cotações) |