Доброго всем здоровья и веселого праздника!
В этом топике я покажу как на Питоне можно извлекать полезную информацию из обычного текста и представлять ее на графиках. Большинство аудитории Смартлаба знают Романа Андреева (2 место по рейтингу, после Создателя) как профессионального трейдера, рекомендациями которого пользуются многие смартлабовцы. Ежедневный утренний топик «Ситуация на текущий момент», стал уже многолетней традицией, как чашка кофе с круассаном, и по-праву набирает огромное количество лайков. Его рекомендации помогают людям не только сохранить свой капитал, но и приумножить его. Я, к сожалению, лично не знаком с Романом, но давно являюсь его подписчиком. А еще, мне нравятся его стихи!
Спасибо Роману за его труд! Я же, постараюсь добавить «наглядности» рекомендациям с помощью кода на Питоне, как всегда в несколько строк.
Итак, за дело! Топик длинный и н ам понадобятся:
1. Любая среда Python (например, Jupyter Notebook Anaconda 3)
2. Вы должны быть подписаны на блог Романа Андреева, т.е. быть его другом на Смартлабе, т.к. доступ к публикации имеют только друзья. Если вы пока не в их числе, то здесь найдете ссылку на 7 прошлых рекомендаций для тестирования кода.
В статье (из кода) вы узнаете:
а) Как добывать исторические котировки по любым инструментам с сайта Финама.
б) Извлекать из обычного текста нужную информацию с помощью регулярных выражений (RE) Python.
в) Представлять извлеченные данные на графиках Matplotlib
Почти каждое утро я на начинаю с чтения блога Романа Андреева «Ситуация на текущий момент». Пока западная часть нашей необъятной Родины находится в объятиях Морфея (короче, спят еще) Роман анализирует за нас ситуацию на мировых площадках и публикует после 7 утра по МСК рекомендации и уровни по основным инструментам финансового рынка: индексам S&P и ММВБ, нефти, золоту, евро-доллару и рубль-доллару. Рекомендация представляет из себя текст, в котором обозначены важные ценовые уровни, зоны поддержки и сопротивления, а также действия в случае развития того или иного сценария. Выглядит примерно так:
Скопируем содержание рекомендации и сохраним его в текстовом файле с названием ГГГГММДД.txt в папку (в нашем коде C:\RomanAndreev), где будет база данных рекомендаций на каждый день. Например, в файле «20201103.txt» будет текст вчерашнего топика.
Чтобы нарисовать график, например СиПи, нам нужно откуда-то взять данные для него. На сегодня такую возможность предоставляет Финам (а мы всё ругаем его). Поэтому, первым делом научимся качать котировки с сайта Финама в Питон. За основу я взял код из топика «Качаем котировки с Финама», который любезно предоставил наш коллега Albus (Игорь Китаев), немного переделав его под собственные нужды. Основная функция GetCandles (ticker, time_frame, period_days), загружает в датафрейм котировки по инструменту ticker, для заданного таймфрейма за последние period_days. Например, вызов micex=GetCandles ('MICEX', «15min», 10) вернет в переменную micex датафрейм, содержащий 15-ти минутные свечи индекса ММВБ за последние 10 дней, начиная с текущего момента времени.
Код майнера, с максимально возможными комментариями внизу:
############################## Мoдуль извлечения котировок с сайта www.finam.ru ########################################### from urllib.parse import urlencode from urllib.request import urlopen from datetime import datetime, timedelta import pandas as pd FINAM_URL = "http://export.finam.ru/" # сервер, на который стучимся #каждому таймфрейму на Финаме соответствует цифровой код: periods={'tick': 1, 'min': 2, '5min': 3, '10min': 4, '15min': 5, '30min': 6, 'hour': 7, 'daily': 8, 'week': 9, 'month': 10} #каждому символу Финам присвоил цифровой код: symbols={'S&P':13944, 'USDRUB': 901, 'ED':83, 'GD':18953, 'MICEX': 420450, 'BZ': 19473, 'ABRD':82460,'AESL':181867,'AFKS':19715,'AFLT':29,'AGRO':399716,'AKRN':17564,'ALBK':82616,'ALNU':81882,'ALRS':81820,'AMEZ':20702,'APTK':13855,'AQUA':35238,'ARMD':19676,'ARSA':19915,'ASSB':16452,'AVAN':82843,'AVAZ':39,'AVAZP':40,'BANE':81757,'BANEP':81758,'BGDE':175840,'BISV':35242,'BISVP':35243,'BLNG':21078,'BRZL':81901,'BSPB':20066,'CBOM':420694,'CHEP':20999,'CHGZ':81933,'CHKZ':21000,'CHMF':16136,'CHMK':21001,'CHZN':19960,'CLSB':16712,'CLSBP':16713,'CNTL':21002,'CNTLP':81575,'DASB':16825,'DGBZ':17919,'DIOD':35363,'DIXY':18564,'DVEC':19724,'DZRD':74744,'DZRDP':74745,'ELTZ':81934,'ENRU':16440,'EPLN':451471,'ERCO':81935,'FEES':20509,'FESH':20708,'FORTP':82164,'GAZA':81997,'GAZAP':81998,'GAZC':81398,'GAZP':16842,'GAZS':81399,'GAZT':82115,'GCHE':20125,'GMKN':795,'GRAZ':16610,'GRNT':449114,'GTLC':152876,'GTPR':175842,'GTSS':436120,'HALS':17698,'HIMC':81939,'HIMCP':81940,'HYDR':20266,'IDJT':388276,'IDVP':409486,'IGST':81885,'IGST03':81886,'IGSTP':81887,'IRAO':20516,'IRGZ':9,'IRKT':15547,'ISKJ':17137,'JNOS':15722,'JNOSP':15723,'KAZT':81941,'KAZTP':81942,'KBSB':19916,'KBTK':35285,'KCHE':20030,'KCHEP':20498,'KGKC':83261,'KGKCP':152350,'KLSB':16329,'KMAZ':15544,'KMEZ':22525,'KMTZ':81903,'KOGK':20710,'KRKN':81891,'KRKNP':81892,'KRKO':81905,'KRKOP':81906,'KROT':510,'KROTP':511,'KRSB':20912,'KRSBP':20913,'KRSG':15518,'KSGR':75094,'KTSB':16284,'KTSBP':16285,'KUBE':522,'KUNF':81943,'KUZB':83165,'KZMS':17359,'KZOS':81856,'KZOSP':81857,'LIFE':74584,'LKOH':8,'LNTA':385792,'LNZL':21004,'LNZLP':22094,'LPSB':16276,'LSNG':31,'LSNGP':542,'LSRG':19736,'LVHK':152517,'MAGE':74562,'MAGEP':74563,'MAGN':16782,'MERF':20947,'MFGS':30,'MFGSP':51,'MFON':152516,'MGNT':17086,'MGNZ':20892,'MGTS':12984,'MGTSP':12983,'MGVM':81829,'MISB':16330,'MISBP':16331,'MNFD':80390,'MOBB':82890,'MOEX':152798,'MORI':81944,'MOTZ':21116,'MRKC':20235,'MRKK':20412,'MRKP':20107,'MRKS':20346,'MRKU':20402,'MRKV':20286,'MRKY':20681,'MRKZ':20309,'MRSB':16359,'MSNG':6,'MSRS':16917,'MSST':152676,'MSTT':74549,'MTLR':21018,'MTLRP':80745,'MTSS':15523,'MUGS':81945,'MUGSP':81946,'MVID':19737,'NAUK':81992,'NFAZ':81287,'NKHP':450432,'NKNC':20100,'NKNCP':20101,'NKSH':81947,'NLMK':17046,'NMTP':19629,'NNSB':16615,'NNSBP':16616,'NPOF':81858,'NSVZ':81929,'NVTK':17370,'ODVA':20737,'OFCB':80728,'OGKB':18684,'OMSH':22891,'OMZZP':15844,'OPIN':20711,'OSMP':21006,'OTCP':407627,'PAZA':81896,'PHOR':81114,'PHST':19717,'PIKK':18654,'PLSM':81241,'PLZL':17123,'PMSB':16908,'PMSBP':16909,'POLY':175924,'PRFN':83121,'PRIM':17850,'PRIN':22806,'PRMB':80818,'PRTK':35247,'PSBR':152320,'QIWI':181610,'RASP':17713,'RBCM':74779,'RDRB':181755,'RGSS':181934,'RKKE':20321,'RLMN':152677,'RLMNP':388313,'RNAV':66644,'RODNP':66693,'ROLO':181316,'ROSB':16866,'ROSN':17273,'ROST':20637,'RSTI':20971,'RSTIP':20972,'RTGZ':152397,'RTKM':7,'RTKMP':15,'RTSB':16783,'RTSBP':16784,'RUAL':414279,'RUALR':74718,'RUGR':66893,'RUSI':81786,'RUSP':20712,'RZSB':16455,'SAGO':445,'SAGOP':70,'SARE':11,'SAREP':24,'SBER':3,'SBERP':23,'SELG':81360,'SELGP':82610,'SELL':21166,'SIBG':436091,'SIBN':2,'SKYC':83122,'SNGS':4,'SNGSP':13,'STSB':20087,'STSBP':20088,'SVAV':16080,'SYNG':19651,'SZPR':22401,'TAER':80593,'TANL':81914,'TANLP':81915,'TASB':16265,'TASBP':16266,'TATN':825,'TATNP':826,'TGKA':18382,'TGKB':17597,'TGKBP':18189,'TGKD':18310,'TGKDP':18391,'TGKN':18176,'TGKO':81899,'TNSE':420644,'TORS':16797,'TORSP':16798,'TRCN':74561,'TRMK':18441,'TRNFP':1012,'TTLK':18371,'TUCH':74746,'TUZA':20716,'UCSS':175781,'UKUZ':20717,'UNAC':22843,'UNKL':82493,'UPRO':18584,'URFD':75124,'URKA':19623,'URKZ':82611,'USBN':81953,'UTAR':15522,'UTII':81040,'UTSY':419504,'UWGN':414560,'VDSB':16352,'VGSB':16456,'VGSBP':16457,'VJGZ':81954,'VJGZP':81955,'VLHZ':17257,'VRAO':20958,'VRAOP':20959,'VRSB':16546,'VRSBP':16547,'VSMO':15965,'VSYD':83251,'VSYDP':83252,'VTBR':19043,'VTGK':19632,'VTRS':82886,'VZRZ':17068,'VZRZP':17067,'WTCM':19095,'WTCMP':19096,'YAKG':81917,'YKEN':81766,'YKENP':81769,'YNDX':388383,'YRSB':16342,'YRSBP':16343,'ZHIV':181674,'ZILL':81918,'ZMZN':556,'ZMZNP':603,'ZVEZ':82001} # Функция запрашивает котировки с сервера экспорта данных Финама по инструменту для заданного таймфрейма за последние # period_days дней и возвращает соответствующий датафрейм def GetCandles (ticker, time_frame, period_days): period=periods[time_frame] #Выбор из: 'tick': 1, 'min': 2, '5min': 3, '10min': 4, '15min': 5, '30min': 6, 'hour': 7, 'daily': 8, 'week': 9, 'month': 10 market = 0 #можно не задавать. Это рынок, на котором торгуется бумага. Для акций работает с любой цифрой. Другие рынки не проверял. # Текущий момент времени end_date = datetime.today() # Время period_days дней назад start_date = end_date - timedelta(days = period_days) #Все параметры упаковываем в единую структуру. Здесь есть дополнительные параметры, кроме тех, которые заданы в шапке. См. комментарии внизу: params = urlencode([ ('market', market), #на каком рынке торгуется бумага ('em', symbols[ticker]), #вытягиваем цифровой символ, который соответствует бумаге. ('code', ticker), #тикер нашей акции ('df', start_date.day), #Начальная дата, номер дня (1-31) ('mf', start_date.month - 1), #Начальная дата, номер месяца (0-11) ('yf', start_date.year), #Начальная дата, год ('from', start_date), #Начальная дата полностью ('dt', end_date.day), #Конечная дата, номер дня ('mt', end_date.month - 1), #Конечная дата, номер месяца ('yt', end_date.year), #Конечная дата, год ('to', end_date), #Конечная дата ('p', period), #Таймфрейм ('f', ticker), #Имя сформированного файла ('e', ".csv"), #Расширение сформированного файла ('cn', ticker), #ещё раз тикер акции ('dtf', 1), #В каком формате брать даты. Выбор из 5 возможных. См. страницу https://www.finam.ru/profile/moex-akcii/sberbank/export/ ('MSOR', 0), #Время свечи (0 - open; 1 - close) ('mstime', "on"), #Московское время ('mstimever', 1), #Коррекция часового пояса ('sep', 1), #Разделитель полей (1 - запятая, 2 - точка, 3 - точка с запятой, 4 - табуляция, 5 - пробел) ('sep2', 1), #Разделитель разрядов ('datf', 1), #Формат записи в файл. Выбор из 6 возможных. ('at', 1)]) #Нужны ли заголовки столбцов url = FINAM_URL + ticker + ".csv?" + params #собственно URL сформированного запроса #Создаем датафрейм candles с котировками candles = pd.read_csv(url) #Добавляем в датафрейм столбец 'DT', который будет содержать время каждой свечи в формате datetime. #Формируем его из столбцов '<DATE>'и '<TIME>', их в последствие можете удалить candles['DT'] = list(map(lambda d,t: ToDatetime(d,t), candles['<DATE>'], candles['<TIME>'])) #Возвращает Датафрейм Пандас со свечами, соответствующими запросу return candles #Преобразует число (или строку) вида 20201030 и строку вида '12:15:00' в объект datetime.datetime(2020, 10, 30, 12, 15) def ToDatetime (date_num, time_hhmmss): return datetime.strptime(str(date_num) + time_hhmmss, '%Y%m%d%H:%M:%S') #Преобразует строку (или число) вида "20201102" в дату (формат datetime) def ToDate (date_yyyymmdd): return datetime.strptime(str(date_yyyymmdd), '%Y%m%d').date()Прекрасно, данные с Финама тянем. Переходим к разборке текста рекомендаций. Мы не будем применять сложные алгоритмы разбора текста на корпуса или прибегать к помощи нейронных сетей, чтобы понять его смысл, а воспользуемся регулярными выражениями (RE) в Питоне, для поиска нужной нам информации. Если посмотреть на текст, то он разбит на абзацы, в которых (но не в каждом) описывается ситуация по тому или иному инструменту (нефти, золоту, индексам и.т.д). Значит первым делом нам нужно понять о каком тикере в абзаце идет речь. Используем функцию similarity(s1, s2) из библиотеки difflib, чтобы определить схожесть одной строки с другой. Одной из строк будут наши токены ('ММВБ', 'Золото', 'СиПи', 'Нефть', 'Евро-доллар', 'Доллар-рубль'), а строками для сравнения первые четыре фразы каждого абзаца. Если схожести нет совсем, то в этом абзаце не упоминаются интересующие нас тикеры и мы пропускаем его. Код реализован в функции what_ticker_about(text).
import difflib import re import time from glob import glob import os import matplotlib.pyplot as plt from textwrap import wrap #В папке path должны лежать все файлы с рекомендациями от Романа Андреева (и не только) по датам #в файлах с именами ГГГГММДД.txt в обычном текстовом формате, например '20201103.txt' path = 'C:\\RomanAndreev\\' #------------------кусок файла приведен ниже:------------------------------------------------------- #Вчера индекс ММВБ закрыл день белой свечкой. Отбившись вниз от низа своего осн канала (на утро 2686) #он выполнил свою первую цель в лице низа более локального канала (на утро 2660), проколов уровень на #... #СиПи подрастает и должен потестить свои сопротивления (на утро — зона 3333-3336): отбой оттуда продаем #с целями 3232, 3168 и 3106, пробой с ретестом покупаем с целями 3402 и 3420. Закрепимся выше 3420 — обновим истхаи. # #Евро-доллар застопорил свое снижение и может потестить снизу пробитые поддержки (на утро 1,1713 и 1,1744): #отбой оттуда или пробой недавнего лоя продаем до зоны 1,1535-1,1502, пробой с ретестом снова покупаем до 1,1942 и 1,2152. #... #Список инструментов, которые анализирует Роман. Тикер - токен: тикер это торгуемый актив, а токен - его привычное название. # Ключ в словаре tickers ДОЛЖЕН СТРОГО СООТВЕТСТВОВАТЬ ключу в словаре symbols (в модуле загрузки котировок с Финама) tickers = {'MICEX' : 'ММВБ', 'GD' : 'Золото', 'S&P' : 'СиПи', 'BZ' : 'Нефть', 'ED' : 'Евро-доллар', 'USDRUB' : 'Доллар-рубль'} ######################## Помощники при семантическом;) разборе предложения для поиска ценовых уровней #################### skip_nums = ["1", "2", "3", "4", "5"] #Эти числа мы исключаем из анализа, если только цена актива не находится рядом с ними #Функция, определяющаа похожесть строк s1 и s2. Чем болше строки похожи друг на друга тем значение будет ближе к 1, иначе к 0 def similarity(s1, s2): matcher = difflib.SequenceMatcher(None, s1.lower(), s2.lower()) return matcher.ratio() #Функция, вернет тикер из списка tickers, если в строке text, в первых четырех словах встречается близкое совпадение #с названием тикера, иначе возвращает False. Т.е. определяет о каком тикере в предложении идет речь. def what_ticker_about (text): words4 = re.split(' ', text, maxsplit=4)[:4] for ticker in tickers: if max(map(lambda w: similarity(w, tickers[ticker]), words4)) > 0.67: return ticker return False #Функция находит в предложении sentence все значимые числа, которые, как мы считаем, могут представлять ценовые уровни, # при этом исключая часто встречаемые числа, например "1-я волна", "2 раза" и т.д. Заодно меняем в числовых значениях # запятые на точки и возвращаем список значений в формате float. def find_levels (sentence): levels = re.findall(r'\d+\,\d+|\d+', sentence) levels_str = [level for level in levels if level not in skip_nums] return list(map(lambda x: float(x.replace(",", ".")), levels_str)) #Определяет диапазоны цен, возвращает список пар (нижняя-верхняя границы диапазона): [['38.5', '38.65'], ['40.25', '40.38']] def find_zones (sentence): zones = re.findall(r'\d+\-\d+|\d+\,\d+\-\d+\,\d+', sentence) return list(map(lambda x: x.replace(",", ".").split('-'), zones)) #################################### Помощники для построения графиков ########################################## #Определяет начальную и конечную позицию Х (по индексу свечей) для заданной даты. Пригодится при отрисовке ценовых уровней def DateX (date, candles): #Цикл по датам в свечах, результат - список X-координат, соответствующих заданной дате xpositions = [index for index, row in candles.iterrows() if row['DT'].date() == date] #Возвращает список - пару начальная координата Х и конечная координата Х для заданной даты на графике if xpositions == []: return [len(candles)-1, len(candles)] #На случай если за текущую дату нет еще свечей return [xpositions[0], xpositions[-1]] #Рисует метки дат на оси Х def PlotDatesX (fig, candles): #Составляем список дат (только уникальные даты) из столбца DT. Они будут метками на оси Х. Сортировка по датам #обязательна, т.к. при создании множества(set) даже из отсортированного списка, множество может не сохранить порядок списка dates = sorted(set(map(lambda dt: datetime.date(dt), candles['DT']))) #Создаем список координат Х для каждой метки (даты). Нам нужна только первая позиция - [0]. xlabel = [DateX(d, candles)[0] for d in dates] #Рисуем ось Х, разделенную по датам fig.set_xticklabels(dates) fig.set_xticks(xlabel) return dates, xlabel #Рисует основной график def draw_candles(candles): #Добавим на график несколько ЕМА-средних candles['ema100'] = pd.Series.ewm(candles['<CLOSE>'], span=100).mean() candles['ema50'] = pd.Series.ewm(candles['<CLOSE>'], span=50).mean() candles['ema20'] = pd.Series.ewm(candles['<CLOSE>'], span=20).mean() plt.style.use('ggplot') #'seaborn-paper' #Отображаем график по цене закрытия свечей и ЕМА-шки fig = candles.plot(y=['<CLOSE>', 'ema50', 'ema20', 'ema100'], figsize=(25,16)) #Добавляем заголовок fig.set_title('График ' + candles['<TICKER>'][0]) #Рисуем шкалу с датами PlotDatesX (fig, candles) #Рисует горизонтальные уровни цен на основном графике, соответствующие уровням рекомендаций на заданную дату def draw_levels (datetime_date, candles, levels): for level in levels: #Левая и правая Х-координата для даты на графике [x1, x2] DX = DateX(datetime_date, candles) plt.plot(DX, [level, level], color = 'dimgray', linewidth = 0.8) #Рисуем зоны на основном графике, соответствующие диапазонам рекомендаций на заданную дату def draw_zones (datetime_date, candles, zones): for zone in zones: #Левая и правая Х-координата для даты на графике [x1, x2] DX = DateX(datetime_date, candles) plt.fill([DX[0], DX[0], DX[1], DX[1]], [float(zone[0]), float(zone[1]), float(zone[1]), float(zone[0])], alpha = 0.3) #Функция завершает отрисовку "сегодняшних" ценовых уровней, подписывая их значения, а также располагает текст #рекомендации на графике. На вход принимает свечи, уровни и текст рекомендации advice def sign_levels (candles, levels, advice): x = len(candles) #Координата Х правого края графика for level in levels: plt.text(x+3, level, str(level), color = 'white', verticalalignment='center', bbox={'facecolor': 'dimgray', 'pad': 2}) price = candles.iloc[-1]['<CLOSE>'] plt.text(x+3, price, str(price), color = 'white', verticalalignment='center', bbox={'facecolor': 'orange', 'pad': 2}) #Пытаемся красиво разместить текст рекомендации. Разобьем ее (рекомендацию) на строчки по 98 символов wrapped = '\n'.join(wrap(advice, width=98)) #Сам текст рекомендации разместим на минимальном по значению уровне и в левой части графика text_ypos = min(levels) plt.text(3, text_ypos, wrapped, fontsize=15, color='dimgray', bbox={'facecolor': 'white', 'alpha': 0,'pad': 2}) #Основная функция для запуска. В папке path должны лежать все файлы с рекомендациями от Романа Андреева (и не только) #по датам в файлах с именами ГГГГММДД.txt в обычном текстовом формате def start_function(path): days_with_levels = 7 #за какое количество дней назад от сегодняшнего показывать уровни рекомендаций days_for_chart = 30 #за какое количество дней строить общий график time_frame = '30min' #какой тайм-фрейм использовать для графика #Проход по всем тикерам в списке рекомендаций for ticker in tickers: last_levels = [] #в этой переменной после окончания цикла будут уровни текущего дня, которые мы подпишем на графике last_advice = '' #а в этой переменной рекомендации на текущий день #Читаем в датафрейм candles свечки с сайта Финама candles = GetCandles (ticker, time_frame, days_for_chart) time.sleep(0.5) #делаем небольшую задержку в запросах к серверу котировок, чтобы нас Финам не забанил draw_candles(candles) #В этой папке path должны лежать все файлы с рекомендациями от Романа Андреева по датам в формате ГГГГММДД.txt for file in sorted(glob(f'{path}\\*.txt'))[-days_with_levels:]: with open(file, 'r') as f: current_date = os.path.splitext(os.path.basename(file))[0] #Читаем файл по абзацам, исключая пустые строки indent = [line.strip() for line in f if line.strip()] #Разбираем абзац на предложения for sentence in indent: #Если находим упоминание тикера в предложениях абзаца found_ticker = what_ticker_about(sentence) if found_ticker == ticker: #Определяем уровни цен для тикера за эту дату levels = find_levels(sentence) zones = find_zones(sentence) last_levels = levels last_advice = sentence print ("Proceccing...", current_date, ticker) draw_levels(ToDate(current_date), candles, levels) draw_zones(ToDate(current_date), candles, zones) #Подписываем уровни "свежих" (сегодняшних) рекомендаций sign_levels(candles, last_levels, last_advice) #Запуск! start_function(path)Итак, подведем итог. Алгоритм действий следующий:
в какой то там волне
уровень жени чернявого только без миллионов
Научите меня!
Проше таблицу на график, которая часто не меняется при застое ...
Реально поржал над разбивкой на блоки тексата и прочим!
Для образовательных целей (типа этой статьи), лучше Колаба не найти.
Для начала несколько инструментов, напр сбер, газ, сипи. Три графика всего, будут лайки можно расширить.
для поклонникам Романа. Пока перенесешь на график уже сессия закончиться :) а тут прямо картинка!
Евгений спасибо за код, с большим уважением отношусь к ребятам кто умеет делать такие вещи!
Просьба -можно подправить код под последние версии Pyton 3.8.2, Pip 20.3.1 и matplotlib до 3.2.3
У меня что та же ошибка
Подскажите, возникает ошибка следующего характера:
UserWarning: FixedFormatter should only be used together with FixedLocator
fig.set_xticklabels(dates)
Как бороться?
Кому будет интересно:
необходимо понизить версию библиотеки matplotlib до 3.2.2
и если пишите в pycharm указать plt.show() в конце кода
тогда все будет работать
с библиотекой естественно Там lua53.dll уже есть
Из пакета QuikSharp, из каталога src\QuikSharp перенес QUIK\LuaScripts\QuikSharp Туда же положил QUIK\LuaScripts\QL
QuikSharp.lua работает А попытки загрузить QL приводят либо той ошибке что привел выше либо error loading module 'socket.core' from file 'C:\Program Files\Lua\5.3\clibs\socket\core.dll':
%1 не является приложением Win32
Ну QuikSharp.lua подгружает библиотеки QuikSharp\lua\clibs64 а QL идет в
C:\Program Files Но у меня там точно x64 в сlibs [clib_luasec-0.6-openssl-1.0.2o-luasocket-3.0-win64.zip] лежит. Так я для проверки в ней package.cpath переписал на каталог из Шарпа Но пока безрезультатно Полазил по инету Таких как я много оказалось…
Я к Квику вернулся много лет спустя по просьбе сына… сам в IB торгую, так что отстал от жизни :)