Parte 1
Na Parte 1 da Análise Técnica Intermediária (ATI) abordaremos:
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(=).
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.
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.
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)
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:
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 | - |
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)
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))
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)
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.
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)
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
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")
Agora podemos usar as cotações de vários anos para analisar um conjunto com bastante ocorrências de sinais de oscilação.
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)