Этот пост — последняя часть из цикла постов посвященных механистической оценке привлекательности инвестиций на основе фундаментальных показателей.
[1] Начало здесь: "Фундаментальный анализ тоже поддается автоматизации и вероятностному прогнозированию"
[2] Продолжение: "От прогноза фин показателей компании к прогнозам возможных цен на бирже"
Итак, вот я и добрался до последней стадии оценки. Как вы понимаете, все те графики, которые я строил и приводил в постах, нужны только для визуализации и более наглядного представления. А еще для написания красивых отчетов по исследованию эмитента и для публичного распространения. Конечно, во многих случаях мне их строить не надо. Вместо этого хотелось бы получить итоговые цифры на основании которых, я бы принял решение — стоит ли овчинка выделки или нет в текущей момент времени.
Но давайте вкратце вспомним, основные промежуточные результаты, которые я получил в первых двух постах. Здесь немного уточню, чтобы расчеты были более корректны. Сама отчетность за 2014 год была составлена 23 марта 2015. Я, конечно, уже не помню, когда она была опубликована, поэтому буду считать что я смог ее посмотреть 31 марта 2015 и провести все те расчеты, которые я демонстрировал в предыдущих постах.
Продолжение статьи "Фундаментальный анализ тоже поддается автоматизации и вероятностному прогнозированию" и не только...
Теперь поговорим немного о мультипликаторах и их использовании в прогнозировании. Нужно понимать, что когда от статей отчетности мы переходим к ценовым мультипликаторам, мы ступаем на очень зыбкую почву. Частично о ловушках того же популярного мультипликатора P/E я уже писал в своем посте: "Дорого или дешево стоят акции на Московской Бирже? И ловушка показателя P/E!", поэтому трактовать моделирование нужно с осторожностью и немалой долей здравого смысла.
Вернусь к примеру по акциям ПАО МАГНИТ.
Мастодонты фондового рынка, такие как Грэм, хорошо понимая недостатки этого показателя советовали при его расчете и принятии решения использовать для среднюю прибыль за 5, 7 или 10 лет, позже Роберт Шиллер выбрал в качестве знаменателя 10-летнюю среднюю прибыль с поправкой на инфляцию. Если огрубить идею инвестиций на основании коэффициента P/E, то можно было бы вывести следующее простое правило:
Я на некоторых своих публичных вебинарах и лекциях рассказывал об элементах в моем подходе при оценке компаний с помощью фундаментального анализа. А также на свое курсе ТРИ КИТА ИНВЕСТИЦИЙ я подробно рассказываю, и показываю в Excel, как и что считать и принимать решение об инвестициях. Для удобства, многие расчеты у меня автоматизированы на Matlab и Python.
Мои оценки эволюционировали со временем, но начинал я как и многие с тех подходов, которые изложены в таких книгах как «Инвестиционная оценка» Дамодорана, «Стоимость компании» Коупленда, Коллера, Муррина и, конечно, «Анализ ценных бумаг» Грэма и Додда. Но мне всегда не давал покоя, тот простой факт, что все подходы, изложенные в этих книгах, не позволяют численно измерить вероятность сделанных оценок. Она неявным образом зашивается в прогнозы темпов роста и в ставки дисконтирования, которые используются для получения справедливой стоимости.
Поэтому я немного модернизировал подход, и сейчас называю его механической оценкой фундаментальных факторов и/или показателей финансово-хозяйственной деятельности предприятия. Помогает мне в этом понимание фундаментальных основ роста компаний, а также знание методов теории вероятности и математической статистики.
from websocket import create_connection import json import random import string import re import pandas as pd import csv from datetime import datetime from time import sleep def filter_raw_message(text): try: found = re.search('"m":"(.+?)",', text).group(1) found2 = re.search('"p":(.+?"}"])}', text).group(1) print(found) print(found2) return found1, found2 except AttributeError: print("error") def generateSession(): stringLength=12 letters = string.ascii_lowercase random_string= ''.join(random.choice(letters) for i in range(stringLength)) return "qs_" +random_string def generateChartSession(): stringLength=12 letters = string.ascii_lowercase random_string= ''.join(random.choice(letters) for i in range(stringLength)) return "cs_" +random_string def prependHeader(st): return "~m~" + str(len(st)) + "~m~" + st def constructMessage(func, paramList): #json_mylist = json.dumps(mylist, separators=(',', ':')) return json.dumps({ "m":func, "p":paramList }, separators=(',', ':')) def createMessage(func, paramList): return prependHeader(constructMessage(func, paramList)) def sendRawMessage(ws, message): ws.send(prependHeader(message)) def sendMessage(ws, func, args): ws.send(createMessage(func, args)) def generate_csv(a): out= re.search('"s":\[(.+?)\}\]', a).group(1) x=out.split(',{\"') with open('data_file.csv', mode='w', newline='') as data_file: employee_writer = csv.writer(data_file, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL) employee_writer.writerow(['index', 'date', 'open', 'high', 'low', 'close', 'volume']) for xi in x: xi= re.split('\[|:|,|\]', xi) print(xi) ind= int(xi[1]) ts= datetime.fromtimestamp(float(xi[4])).strftime("%Y/%m/%d, %H:%M:%S") employee_writer.writerow([ind, ts, float(xi[5]), float(xi[6]), float(xi[7]), float(xi[8]), float(xi[9])]) # Initialize the headers needed for the websocket connection headers = json.dumps({ # 'Connection': 'upgrade', # 'Host': 'data.tradingview.com', 'Origin': 'https://data.tradingview.com' # 'Cache-Control': 'no-cache', # 'Upgrade': 'websocket', # 'Sec-WebSocket-Extensions': 'permessage-deflate; client_max_window_bits', # 'Sec-WebSocket-Key': '2C08Ri6FwFQw2p4198F/TA==', # 'Sec-WebSocket-Version': '13', # 'User-Agent': 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36 Edg/83.0.478.56', # 'Pragma': 'no-cache', # 'Upgrade': 'websocket' }) # Then create a connection to the tunnel ws = create_connection( 'wss://data.tradingview.com/socket.io/websocket',headers=headers) session= generateSession() print("session generated {}".format(session)) chart_session= generateChartSession() print("chart_session generated {}".format(chart_session)) # Then send a message through the tunnel sendMessage(ws, "set_auth_token", ["unauthorized_user_token"]) sendMessage(ws, "chart_create_session", [chart_session, ""]) sendMessage(ws, "quote_create_session", [session]) sendMessage(ws,"quote_set_fields", [session,"ch","chp","current_session","description","local_description","language","exchange","fractional","is_tradable","lp","lp_time","minmov","minmove2","original_name","pricescale","pro_name","short_name","type","update_mode","volume","currency_code","rchp","rtc"]) sendMessage(ws, "quote_add_symbols",[session, "BINANCE:BTCUSDT", {"flags":['force_permission']}]) sendMessage(ws, "resolve_symbol", [chart_session, "symbol_1","={\"symbol\":\"BINANCE:BTCUSDT\",\"adjustment\":\"splits\"}"]) sendMessage(ws, "create_series", [chart_session,"s1","s1","symbol_1","1",300]) sendMessage(ws, "quote_fast_symbols", [session,"BINANCE:BTCUSDT"]) sendMessage(ws, "create_study", [chart_session,"st1","st1","s1","Volume@tv-basicstudies-118",{"length":20,"col_prev_close":"false"}]) sendMessage(ws, "quote_hibernate_all", [session]) #st='~m~140~m~{"m":"resolve_symbol","p":}' #p1, p2 = filter_raw_message(st) #sendMessage(ws, "resolve_symbol", [chart_session,"symbol_1","={\"symbol\":\"BINANCE:BTCUSDT\",\"adjustment\":\"splits\",\"session\":\"extended\"}"]) #sendMessage(ws, "create_series", [chart_session, "s1", "s1", "symbol_1", "1", 2]) #sendMessage(ws, "create_study", [chart_session,"st4","st1","s1","ESD@tv-scripting-101!",{"text":"BNEhyMp2zcJFvntl+CdKjA==_DkJH8pNTUOoUT2BnMT6NHSuLIuKni9D9SDMm1UOm/vLtzAhPVypsvWlzDDenSfeyoFHLhX7G61HDlNHwqt/czTEwncKBDNi1b3fj26V54CkMKtrI21tXW7OQD/OSYxxd6SzPtFwiCVAoPbF2Y1lBIg/YE9nGDkr6jeDdPwF0d2bC+yN8lhBm03WYMOyrr6wFST+P/38BoSeZvMXI1Xfw84rnntV9+MDVxV8L19OE/0K/NBRvYpxgWMGCqH79/sHMrCsF6uOpIIgF8bEVQFGBKDSxbNa0nc+npqK5vPdHwvQuy5XuMnGIqsjR4sIMml2lJGi/XqzfU/L9Wj9xfuNNB2ty5PhxgzWiJU1Z1JTzsDsth2PyP29q8a91MQrmpZ9GwHnJdLjbzUv3vbOm9R4/u9K2lwhcBrqrLsj/VfVWMSBP","pineId":"TV_SPLITS","pineVersion":"8.0"}]) # Printing all the result a="" while True: try: sleep(1) result = ws.recv() pattern = re.compile("~m~\d+~m~~h~\d+$") if pattern.match(result): ws.recv() ws.send(result) print("\n\n\n hhhhhhhhhhhhhhhhhhhhhh "+ str(result) + "\n\n") print(result) a=a+result+"\n" except Exception as e: print(e) break generate_csv(a)'https://github.com/rushic24/tradingview-scraper'
Всем здоровья и бодрого расположения духа!
В статье «Визуализация рекомендаций Романа Андреева на Python» мы разобрали как можно с помощью нескольких строк кода на Питоне разобрать текст, который выкладывает каждое утро в своем блоге Роман Андреев (далее по тексту Роман) — известный трейдер и блогер (или наоборот), и отобразить эти рекомендации в виде уровней и зон на графиках. В этом топике я покажу способ для извлечения информации из графических изображений с помощью технологий компьютерного зрения (но без использования нейронных сетей) на примере таблиц-рекомендаций из блога Романа Андреева.
Надеюсь, что я не напугал читателей термином «компьютер вижн», скоро вы поймете, что это просто. И что любой юный прогер может написать код для распознавания внешними камерами номеров автомобилей, который впоследствии возненавидят все автолюбители мегаполисов, а МАДИ и ГИБДД будут собирать со всех нас миллиардные штрафы