Я хочу включить динамический «период просмотра» для моих фондовых индикаторов для заданного периода времени.Ранее я реализовал Homodyne Discriminator от Ehler, используя скользящее окно ;каждый раз, когда в моем алгоритме появляется новая точка данных, дискриминатор пересчитывается (но сохраняет память о предыдущих вычислениях ... см. ниже).Я бы предпочел определить период с использованием Pandas, так как он кажется более быстрым методом реализации обработки данных для больших наборов данных.
Обратите внимание, что я сталкиваюсь с данными двумя способами: во-первых, исторические данные генерируются в большом количестве;и, во-вторых, данные поступают по одной минуте за раз и будут добавляться к историческим данным для повторной обработки.
Проблемы, с которыми я сталкиваюсь:
- Расчеты зависят отЗначение индекса периода и периода зависит от других расчетов (см. оригинальный скрипт).Однако в настоящее время вычисления с использованием панд выполняются в большом количестве, поэтому данные никогда не изменяются со временем, как это должно быть.
- В кадре данных содержатся значения для нескольких активов (MultiIndex), поэтому в настоящее время я обрабатываю дискриминатор один раз для каждого актива;Есть ли способ, которым я могу выполнить это один раз и позволить Pandas выполнить группировку?
- Должен ли я просто повторно обрабатывать весь набор данных каждый раз, когда поступают новые данные, или я должен покончить с преимуществами Pandas и просто выполнить итерациюкаждая новая строка и использовать мой старый скрипт?
Исторические данные:
close high low open volume
symbol time
SPY 2019-06-07 15:41:00 288.03 288.060 287.98 288.030 132296.0
2019-06-07 15:42:00 288.04 288.060 287.96 288.035 103635.0
2019-06-07 15:43:00 288.15 288.160 288.04 288.045 144841.0
2019-06-07 15:44:00 288.10 288.190 288.09 288.150 166086.0
2019-06-07 15:45:00 287.93 288.120 287.93 288.100 145304.0
2019-06-07 15:46:00 287.77 287.935 287.75 287.935 253202.0
2019-06-07 15:47:00 287.86 287.870 287.76 287.760 140996.0
2019-06-07 15:48:00 287.78 287.865 287.76 287.860 178082.0
2019-06-07 15:49:00 287.83 287.855 287.62 287.790 631133.0
2019-06-07 15:50:00 287.83 287.915 287.78 287.825 279326.0
Оригинальный скрипт (self.Value - фактический период).Если вы не используете QuantConnect, я уверен, что вы можете просто заменить все RollingWindows на массивы с обращенными данными или отменить ссылки.В этом сценарии Update
вызывается каждый раз, когда в фрейме данных создается новая строка:
class HomodyneDiscriminatorPeriodOld():
Values = RollingWindow[int](2)
SmoothedPeriod = RollingWindow[float](2)
Smooth = RollingWindow[float](7)
Detrend = RollingWindow[float](7)
Source = RollingWindow[float](4)
I1 = RollingWindow[float](7)
I2 = RollingWindow[float](7)
Q1 = RollingWindow[float](7)
Q2 = RollingWindow[float](7)
Re = RollingWindow[float](2)
Im = RollingWindow[float](2)
def FillWindows(self, *args, value=0):
for window in args:
for i in range(window.Size):
window.Add(value)
def __init__(self, period=1):
self.Value = period
self.Period = period
# Start with history
self.FillWindows(self.Smooth, self.SmoothedPeriod, self.Detrend, self.I1, self.I2, self.Q1, self.Q2, self.Re, self.Im)
self.FillWindows(self.Values, value=self.Value)
def __repr__(self):
return "{}".format(self.Value)
def Weighted(self, first, second, percent=0.2):
return percent * first + (1 - percent) * second
def Quadrature(self, window):
C1 = 0.0962
C2 = 0.5769
C3 = self.Period * 0.075 + 0.54
return (window[0] * C1 + window[2] * C2 - window[4] * C2 - window[6] * C1) * C3
def Update(self, data):
self.Source.Add((data.High + data.Low) / 2)
if not self.Source.IsReady: return self.Value
#
# --- Start the Homodyne Discriminator Caculations
#
# Mutable Variables (non-series)
self.Smooth.Add((self.Source[0] * 4.0 + self.Source[1] * 3.0 + self.Source[2] * 2.0 + self.Source[3]) / 10.0)
self.Detrend.Add(self.Quadrature(self.Smooth))
# Compute InPhase and Quadrature components
self.Q1.Add(self.Quadrature(self.Detrend))
self.I1.Add(self.Detrend[3])
# Advance Phase of I1 and Q1 by 90 degrees
jI = self.Quadrature(self.I1)
jQ = self.Quadrature(self.Q1)
# Phaser addition for 3 bar averaging and
# Smooth i and q components before applying discriminator
self.I2.Add(self.Weighted(self.I1[0] - jQ, self.I2[0]))
self.Q2.Add(self.Weighted(self.Q1[0] + jI, self.Q2[0]))
# Extract Homodyne Discriminator
self.Re.Add(self.Weighted(self.I2[0] * self.I2[1] + self.Q2[0] * self.Q2[1], self.Re[0]))
self.Im.Add(self.Weighted(self.I2[0] * self.Q2[1] - self.Q2[0] * self.I2[1], self.Im[0]))
# Calculate the period
period = ((math.pi * 2) / math.atan(self.Im[0] / self.Re[0])) if (self.Re[0] != 0 and self.Im[0] != 0) else 0
period = min(max(max(min(period, 1.5 * self.Period), 0.6667 * self.Period), 6), 50)
self.Period = self.Weighted(period, self.Period)
self.SmoothedPeriod.Add(self.Weighted(self.Period, self.SmoothedPeriod[0], 0.33))
self.Value = round(self.SmoothedPeriod[0] * 0.5 - 1)
if self.Value < 1: self.Value = 1
self.Values.Add(self.Value)
return self.Value
Pandas Script.Update
в настоящее время вызывается только один раз после массового импорта исторических данных.Я до сих пор не реализовал метод расчета, как указано в Q3, если он даже требуется:
class HomodyneDiscriminatorPeriod():
def Weighted(self, series, other=None, percent=0.2):
if other is None: other = series
return percent * series + (1 - percent) * other
def Quadrature(self, series):
C1 = 0.0962
C2 = 0.5769
C3 = self.Frame.period * 0.075 + 0.54
return (series * C1 + series.shift(2) * C2 - series.shift(4) * C2 - series.shift(6) * C1) * C3
def Update(self, frame):
# Add period column to timeframe's dataframe
frame['period'] = 1
# Initialize internal dataframe with same structure
# as timeframe's dataframe but without original columns
self.Frame = pd.DataFrame().reindex_like(frame)
self.Frame.drop(frame.columns, axis=1)
self.Frame['period'] = 1
self.Frame['smoothed_period'] = 1
self.Frame['i2'] = 0
self.Frame['q2'] = 0
self.Frame['re'] = 0
self.Frame['im'] = 0
# Shorthand references
period = self.Frame['period']
smoothed_period = self.Frame['smoothed_period']
i2 = self.Frame['i2']
q2 = self.Frame['q2']
re = self.Frame['re']
im = self.Frame['im']
#
# --- Start the Homodyne Discriminator Caculations
#
# Mutable Variables (non-series)
hl2 = (frame.high + frame.low) / 2
smooth = (hl2 * 4.0 + hl2.shift(1) * 3.0 + hl2.shift(2) * 2.0 + hl2.shift(3)) / 10.0
detrend = self.Quadrature(smooth)
# Compute InPhase and Quadrature components
q1 = self.Quadrature(detrend)
i1 = detrend.shift(3)
# Advance Phase of I1 and Q1 by 90 degrees
ji = self.Quadrature(i1)
jq = self.Quadrature(q1)
# Phaser addition for 3 bar averaging and
# smooth i and q components before applying discriminator
i2 = self.Weighted(i1 - jq)
q2 = self.Weighted(q1 + ji)
# Extract Homodyne Discriminator
re = self.Weighted(i2 * i2.shift(1) + q2 * q2.shift(1))
im = self.Weighted(i2 * q2.shift(1) - q2 * i2.shift(1))
# Calculate the period
# TODO: Use 360 or 2 * np.pi???? Official doc says 360...
_period = (2 * np.pi / np.arctan(im / re)).clip(upper=1.5 * period, lower=0.6667 * period).clip(upper=50, lower=6)
period = self.Weighted(_period, period)
smoothed_period = self.Weighted(period, smoothed_period, 0.33)
return (smoothed_period * 0.5 - 1).round().clip(lower=1)