Как организовать структуру классов?

#python #class #structure

#python #класс #структура

Вопрос:

Я программирую советник для торговли на валютном рынке (бинарные опционы). Использование FXCM API. Для тех, кому интересно. И у меня есть 5 валютных пар. Каждый из которых содержит историю котировок в пяти разных временных рамках. Например, скажем, от одной минуты до пяти минут. Каждая история должна иметь три переменные. (самый большой, самый маленький, последний) История котировок загружается из FXCM один раз за период времени, равный одной минуте, а затем вычисляется для всех остальных временных рамок. Также у меня есть методы (или, может быть, я могу объединить их в один метод) для фактических данных, которые выполняются раз в минуту, каждые две минуты и так далее, соответственно, для записи вновь сформированной свечи в соответствующей истории. Я приложу структуру классов, которую я вижу, но которая мне не нравится.

 class Pair:    
    def __init__(self, name):
        self.name = name

class Periods:     
    def __init__(self, name):
        self.name  = name
        self.one   = pd.DataFrame()
        self.two   = pd.DataFrame()
        self.three = pd.DataFrame()
        self.four  = pd.DataFrame()
        self.five  = pd.DataFrame()

    def get_historical_data(self): # Executed once
        self.one   = historical_data_one_minute
        self.two   = historical_data_two_minute
        self.three = historical_data_three_minute
        self.four  = historical_data_four_minute
        self.five  = historical_data_five_minute

    def set_actual_data(self, period): #Executed ones per minute
        if period == '1min':
            one_minute_candle = calculated_one_minute_candle
            self.one   = self.one.append(one_minute_candle)
        if period == '2min':
            two_minute_candle = calculated_two_minute_candle
            self.two   = self.two.append(two_minute_candle)
        ...

        if period == '5min':
            five_minute_candle = calculated_five_minute_candle
            self.five   = self.five.append(five_minute_candle)
  

Как вы заметили, здесь нет переменных biggest, smallest и last . Это потому, что я не знаю, как их связать. Должен ли я разбивать «Периоды» на классы, такие как One_minute, Two_minute и так далее
, Чтобы добавлять переменные или есть другой способ? Будет ли слишком много классов для этого?

 class One_minute:
    def __init__(self):
        self.largest = 0
        self.smallest = 0
        self.last = 0

    def get_historical_data(self):
        self.history = historical_data_one_minute
        self.largest = calculated_largest_from_historical_data_one_minute
        self.smallest = calculated_smallest_from_historical_data_one_minute
        self.last = calculated_last_from_historical_data_one_minute
  

В целом структура выглядит следующим образом

 (currency pair) - one_minute_history ---- largest
                |                    |___ smallest
                |                    |___ last
                |_two_minute_history ---- largest
                |                    |___ smallest
                |                    |___ last
                |_and so on
  

Или

 (currency pair) ------ one_minute_history
                 |  |_ largest
                 |  |_ smallest
                 |  |_ last
                 |____ two_minute_history
                 |  |_ largest
                 |  |_ smallest
                 |  |_ last
                 |____ three_minute_history
                  ...
  

Было бы удобно, если бы доступ осуществлялся так

 eurusd.history[1] #gets one minute history
eurusd.history[1].largest or eurusd.largest.history[1]  #gets largest price of one minute history
  

Или это может быть что-то лучше.

В общей сложности получается, что у меня будет 25 разных фреймов данных истории, по 5 для каждой валютной пары. А также для каждого фрейма данных истории у меня будет три переменные

Надеюсь, я мог бы объяснить, что мне нужно получить. Заранее благодарю!

Редактировать: я думаю, что неважно, как вычисляются исторические данные, но отвечая на ваш вопрос — вот этот метод

     def get_historical_data(self, con): # Once
        dtn     = datetime.now()  
        hours   = dtn.hour   3
        minutes = dtn.minute
    
        data = con.get_candles(instrument, period = 'm1', start=dtn - timedelta(hours=hours, minutes=minutes), end=dtn)        
        self.one['open']      = (data['bidopen']    data['askopen'])  / 2
        self.one['high']      = (data['bidhigh']    data['askhigh'])  / 2
        self.one['low']       = (data['bidlow']     data['asklow'])   / 2
        self.one['close']     = (data['bidclose']   data['askclose']) / 2
        self.one['direction'] = 'flat'
        self.one.loc[self.one['close'] - self.one['open'] >=  0.00002, "direction"] = "up"
        self.one.loc[self.one['close'] - self.one['open'] <= -0.00002, "direction"] = "down"
    
        s = (self.one['direction'] != self.one['direction'].shift()).cumsum()
        
        self.one['series'] = self.one.groupby(s).cumcount()   1
        self.one.loc[self.one['direction'] == 'flat', 'series'] = self.one['series'].subtract(1)
        self.one = self.one.tz_localize(pytz.timezone('UTC'))
        self.one = self.one.tz_convert(pytz.timezone('Europe/Moscow'))    
        self.one.to_csv('history.csv', index=True)          
    
        dct = {'date': 'first', 'open': 'first', 'high': 'max', 'low': 'min', 'close': 'last'}
        
        self.two   = self.one.reset_index().groupby(np.arange(len(self.one)) // 2).agg(dct).set_index('date')
        self.three = self.one.reset_index().groupby(np.arange(len(self.one)) // 3).agg(dct).set_index('date')
        self.four  = self.one.reset_index().groupby(np.arange(len(self.one)) // 4).agg(dct).set_index('date')
        self.five  = self.one.reset_index().groupby(np.arange(len(self.one)) // 5).agg(dct).set_index('date')
  

Функция вызывается в цикле.

 con = fxcmpy.fxcmpy(config_file = 'fxcm.cfg') # Connection
periods = Periods("EUR/USD")
cycle_just_launched = True
while True:
    time.sleep(60)
    if cycle_just_launched:
        periods.get_historical_data(con)
    else:
        periods.set_actual_data(con, "1min")
    cycle_just_launched = False
  

Комментарии:

1. Как рассчитывается свеча 1/2/3/4/5 минут и кто отвечает за вызов этой функции? Откуда historical_data берется?

2. Я отредактировал сообщение, чтобы ответить на ваши вопросы

Ответ №1:

Похоже, вам нужно инкапсулировать концепцию «истории с одним периодом», а затем вы можете построить остальное поверх этого. Что-то вроде:

 class PeriodHistory:
    def __init__(self, name, period):
        self.name = name
        self.period = period # e.g. 1min, 2min, etc.
        self.data = pd.DataFrame()
        self.largest = 0
        self.smallest = 0
        self.latest = 0

    def load(self, historic_data):
        # initialize data, largest, smallest, latest from historical

    def update(self, new_data):
        # update variables based on new data and self.period
  

Затем класс для объединения многопериодных историй в одну:

 class MultiPeriodHistory:
    def __init__(self, name):
        self.name = name
        self.period = {}
        for i in range(1, 6):
            self.period[i] = PeriodHistory(self.name, i)
            self.period[i].load(history_data_for_period[i])

    def update(self):
        new_data = get_data_from_api(self.name)
        for i in range(1, 6):
            self.period[i].update(new_data)
  

И тогда вы можете использовать dict для хранения мультиистории для каждой валютной пары:

 currencies = ('eur', 'usd', 'jpy', 'gbp', 'chf')
history = {}
for c1 in currencies:
    for c2 in currencies:
        if c1 != c2:
            history[c1   c2] = MultiPeriodHistory(c1   c2)
  

Теперь, например, history['eurgbp'].period[3].smallest даст вам то, что вы хотите.

Возможно, вам потребуется изменить выборку данных и немного обновить, чтобы соответствовать этому шаблону, я много махал рукой, поскольку у меня нет полной картины.

Комментарии:

1. Выглядит довольно красиво. Спасибо 🙂