Как и обещал ранее некоторым участникам, сейчас продемонстрирую код, с помощью которого можно визуализировать свечной график, данные для которого будет взят с сайта Финам. Самое прамолинейное решение — это найти какой-нибудь модуль для питона, которому скармливаются бары, а он тебе выдает, собственно, свечной график. Такие есть, но на тот момент, когда я интересовался темой, найденное меня не устроило. Например, свечной график мне нарисуют, а как на нем тот же индикатор отрисовать — уже проблема. А если надо задать какую-нибудь эдакую линию, маркер, цвет — с этим надо разбираться. Но зачем тратить на это время, если есть весьма добротный модуль для построения графиков Matplotlib, с помощью него можно сделать любой график полиграфического качества, который у тебя в любое издание примут без вопросов, если, конечно, там и смысловая составляющая на должном уровне, само собой. В общем, качаем скрипт отсюда:
yadi.sk/d/fiMn-YUtrB6aEw
если не установлено, устанавливаем python 3.5+, к нему matplotlib и numpy, запускаем скрипт и умиляемся результату))
Окно с графиком, которое выдает скрипт позволяет выделять область, например:
растягивать ее на всю область графика:
и двигать график относительно просматриваемой прямоугольной области.
В принципе, этого функционала вполне достаточно для того, чтобы в одном лишь питоне сделать всю работу по созданию, тестированию и отлаживанию торговой стратегии. Очень удобно рисовать наклонные линии сделок на графике (например, зеленая линия для прибыльного открытия-закрытия позиции и красная для убыточного) и анализировать их входы на предмет как корректности технической составляющей алгоритма стратегии, так и самой торговой идеи. Например, в случае тестера метатрейдера на выходе получаешь много всякой статистики, но анализировать причину убыточности сделок там не так удобно (я таким занимался в начале 2016 года, может с тех пор тестер метатрейдера поменялся).
При желании, можно визуализировать любые другие исторические данные по свечам в формате, который выдает Финам (по тому же яндексу данные качаются здесь:
www.finam.ru/profile/moex-akcii/pllc-yandex-n-v/export/).
У скрипта есть три входных параметра (строчки 9, 91 и 92):
dataFile = 'YNDX_190114_190115.csv'
timeZoneDiffSecs = 3 * 3600
pt = 0.01
которые задают файл с данными от Финама, разницу между поясом, для времени которого хотите представить график и UTC, а также минимальный шаг цены инструмента.
Технологически, каждая свеча прорисовывается как errorbar (кресты ошибок), дважды — один раз для тела свечи, другой раз для ее тени. В первом случае отрисовываются горизонтальные линии отмечающие границы тела свечи, во втором эти линии отсутствуют, как у привычного вида тени свечи:
plt.errorbar( xs[ blackBars ], totalCentres[ blackBars ], yerr = totalSpans[ blackBars ], ecolor = 'k', elinewidth = 0.5, capsize = 0, ls = 'none' )
plt.errorbar( xs[ blackBars ], bodyCentres[ blackBars ], yerr = np.abs( bodySpans[ blackBars ] ), ecolor = 'k', elinewidth = 0.5, capsize = 2, ls = 'none' )
Еще, чтобы разобраться в этом коде, надо знать такие аспекты numpy как slicing, например, вот здесь используется:
bodyCentres = 0.5 * ( bars[ :, 1 ] + bars[ :, 4 ] )
bodySpans = 0.5 * ( bars[ :, 4 ] — bars[ :, 1 ] )
totalCentres = 0.5 * ( bars[ :, 2 ] + bars[ :, 3 ] )
totalSpans = 0.5 * ( bars[ :, 2 ] — bars[ :, 3 ] )
и boolean indexing, вот пример использования:
blackBars = np.abs( bodySpans ) < 0.25 * pt
greenBars = np.logical_and( np.logical_not( blackBars ),
bodySpans >= 0.25 * pt )
redBars = np.logical_not( np.logical_or( blackBars, greenBars ) )
вот, собственно, сам скрипт:
import os.path, time
import numpy as np
import matplotlib.style
import matplotlib as mpl
mpl.style.use('classic')
mpl.rcParams['axes.formatter.useoffset'] = False
import matplotlib.pyplot as plt
dataFile = 'YNDX_190114_190115.csv'
#dataFile = 'YNDX_190114_190115-1.csv'
#dataFile = 'YNDX_190114_190115-2.csv'
#dataFile = 'YNDX_190114_190115-3.csv'
#dataFile = 'YNDX_181201_190115-4.csv'
def my_split( s, seps ): # this function splits line to parts separated with given separators
res = [s]
for sep in seps:
s, res = res, []
for seq in s:
res += seq.split( sep )
i = 0
while i < len( res ):
if res[i] == '':
res.pop(i)
continue
i += 1
return res
def loadFinamCsv( fname ):
if not os.path.isfile( fname ):
raise ValueError( 'wrong file name: %s' % fname )
counter = 0
fi = open( fname, 'r' )
tickerNameIsFound = False
for line in fi: # this loop counts number of bars and reads ticker name from the first bar
firstSymbol = line[ :1 ]
if firstSymbol == '' or firstSymbol == '<':
continue
if not tickerNameIsFound:
parsed = my_split( line, ',\n' )
ticker = parsed[0]
period = parsed[1]
tickerNameIsFound = True
counter += 1
bars = np.zeros( ( counter, 6 ), dtype = np.float64 ) # create matrix for reading the whole file
print( counter )
fi.seek( 0, 0 ) # move file pointer to the beginning
counter = 0
for line in fi:
firstSymbol = line[ :1 ]
if firstSymbol == '' or firstSymbol == '<':
continue
parsed = my_split( line, ';,\n' )
timeStamp = parsed[2] + parsed[3]
dtime = time.strptime( timeStamp + '+0300', '%Y%m%d%H%M%S%z' )
timeEpoch = time.mktime( dtime )
bars[ counter, : ] = np.array( ( timeEpoch, np.float64(parsed[4]), np.float64(parsed[5]),
np.float64(parsed[6]), np.float64(parsed[7]), np.float64(parsed[8]) ) )
counter += 1
if counter % 1000 == 0:
print( int( counter / 1000 ), end = ' ' )
fi.close()
print( '\n' )
return { 'ticker': ticker, 'period': period, 'bars': bars }
def convertPeriodString( periodRaw ):
try:
numMins = int( periodRaw )
if numMins % 60 != 0:
return 'M%d' % numMins
else:
return 'H%d' % ( numMins // 60 )
except ValueError:
return periodRaw
timeZoneDiffSecs = 3 * 3600 # we need to know in advance the time zone difference between UTC
pt = 0.01 # we need to know in advance the min price step (point)
data = loadFinamCsv( dataFile )
ticker = data[ 'ticker' ]
period = data[ 'period' ]
bars = data[ 'bars' ]
periodFine = convertPeriodString( period )
xs = np.array( range( bars.shape[0] ) )
bodyCentres = 0.5 * ( bars[ :, 1 ] + bars[ :, 4 ] )
bodySpans = 0.5 * ( bars[ :, 4 ] - bars[ :, 1 ] )
totalCentres = 0.5 * ( bars[ :, 2 ] + bars[ :, 3 ] )
totalSpans = 0.5 * ( bars[ :, 2 ] - bars[ :, 3 ] )
blackBars = np.abs( bodySpans ) < 0.25 * pt
greenBars = np.logical_and( np.logical_not( blackBars ),
bodySpans >= 0.25 * pt )
redBars = np.logical_not( np.logical_or( blackBars, greenBars ) )
plt.clf()
plt.errorbar( xs[ blackBars ], totalCentres[ blackBars ], yerr = totalSpans[ blackBars ], ecolor = 'k', elinewidth = 0.5, capsize = 0, ls = 'none' )
plt.errorbar( xs[ blackBars ], bodyCentres[ blackBars ], yerr = np.abs( bodySpans[ blackBars ] ), ecolor = 'k', elinewidth = 0.5, capsize = 2, ls = 'none' )
plt.errorbar( xs[ greenBars ], totalCentres[ greenBars ], yerr = totalSpans[ greenBars ], ecolor = 'g', elinewidth = 0.5, capsize = 0, ls = 'none' )
plt.errorbar( xs[ greenBars ], bodyCentres[ greenBars ], yerr = np.abs( bodySpans[ greenBars ] ), ecolor = 'g', elinewidth = 0.5, capsize = 2, ls = 'none' )
plt.errorbar( xs[ redBars ], totalCentres[ redBars ], yerr = totalSpans[ redBars ], ecolor = 'r', elinewidth = 0.5, capsize = 0, ls = 'none' )
plt.errorbar( xs[ redBars ], bodyCentres[ redBars ], yerr = np.abs( bodySpans[ redBars ] ), ecolor = 'r', elinewidth = 0.5, capsize = 2, ls = 'none' )
plt.xlabel( 'Bar No., %s' % periodFine )
plt.ylabel( ticker ) # before was: fname[ : fname.find( ' ' ) ]
plt.xlim( xs[0] - 0.5, xs[-1] + 0.5 )
plt.annotate( 'start: %s' % ( time.strftime( '%Y-%m-%d %H:%M:%S', time.gmtime( bars[ 0, 0 ] + timeZoneDiffSecs ) ) ),
xy=(0.1,0.95), xycoords='axes fraction',
fontsize=11, horizontalalignment='left', verticalalignment='top' )
plt.annotate( 'end: %s' % ( time.strftime( '%Y-%m-%d %H:%M:%S', time.gmtime( bars[ -1, 0 ] + timeZoneDiffSecs ) ) ),
xy=(0.1,0.90), xycoords='axes fraction',
fontsize=11, horizontalalignment='left', verticalalignment='top' )
plt.savefig( dataFile[ : dataFile.rfind( '.' ) ] + '.png')
plt.show()
В скрипте присутствует всего несколько комментариев, но, думаю, кому это действительно покажется полезным, сам разберется. Если что — задавайте вопросы. Буду рад, если этот мой небольшой труд кому-то поможет вывести какую-нибудь гениальную торговую идею, чего всем и желаю))
незнал про этот модуль.
Есть plotly тоже рисует графики, причем может сгенерить html с ява скриптом и масштабированием, а может вывести в jupiter.