Каков наилучший способ исправить «IFs ад»? - PullRequest
2 голосов
/ 19 мая 2019

У меня есть класс сделки, который имеет несколько состояний: новые, купленные, завершенные, трендовые и перекрестные. Экземпляр сделки может изменить тренд и пересечение в любое время - в зависимости от состояния рынка.

Зависит от текущей "тенденции", "пересечения" и, возможно, других условий сделки могут быть обработаны по-разному

Я пробовал шаблоны «Стратегия и цепь ответственности», но моих знаний и опыта недостаточно, чтобы сделать это правильно. И я не уверен, что принял правильное решение.

Теперь это работает, но это чертовски просто так

async def check_deal(deal):

    if deal.is_new():
        if deal.current_trend == UP_TREND and CROSSED_TOP_GREEN in deal.crossing:
            return await deal.create_order(CROSSED_TOP_GREEN)

        if deal.current_trend == FLAT_TREND and CROSSED_BOTTOM_ORANGE in deal.crossing:
            return await deal.create_order(CROSSED_BOTTOM_ORANGE)

        if deal.current_trend == DOWN_TREND and CROSSED_BOTTOM in deal.crossing:
            return await deal.create_order(CROSSED_BOTTOM)

    if deal.is_bought() and not deal.candle_has_order:
        if deal.current_trend == UP_TREND and CROSSED_BOTTOM_GREEN in deal.crossing:
            return await deal.create_order(CROSSED_BOTTOM_GREEN)
        else:
            return await deal.update_deal()

        if deal.current_trend == DOWN_TREND and CROSSED_BOTTOM in deal.crossing:
            return await deal.create_order(CROSSED_BOTTOM)
        else:
            return await deal.update_deal()

    else:
        return await deal.update_deal()

    if deal.is_profitable() and len(deal.orders) == 1 :
        if deal.current_trend == UP_TREND  and CROSSED_TOP in deal.crossing:
            return await deal.trailing_enable(CROSSED_TOP)

        if deal.current_trend == FLAT_TREND and CROSSED_TOP_GREEN in deal.crossing:
            return await deal.trailing_enable(CROSSED_TOP_GREEN)

        if deal.current_trend == DOWN_TREND and CROSSED_BASIS in deal.crossing:
            return await deal.trailing_enable(CROSSED_BASIS)

    if deal.is_profitable() and len(deal.orders) > 1:
        if deal.current_trend == UP_TREND and CROSSED_BASIS in deal.crossing:
            return await deal.trailing_enable(CROSSED_BASIS)

        if deal.current_trend == DOWN_TREND and  CROSSED_BOTTOM_GREEN in deal.crossing:
            return await deal.trailing_enable(CROSSED_BOTTOM_GREEN)

Правила

текущее пересечение является переменной и может отличаться в любое время текущий тренд является переменной и может отличаться в любое время

Мы начинаем дело в одном из случаев:

  1. Deal.is_new () (сделка не сохранена в базе данных), а текущий тренд - UP, а текущий переход - в deal.crossing
  2. Deal.is_new () и текущий тренд - FLAT, а текущий пересечение в сделке. Пересечение
  3. Deal.is_new (), текущий тренд - ВНИЗ, а текущий переход - в сделке. Скрещивание

Мы добавляем средства к текущей сделке в одном из случаев:

  1. Deal.is_bought () (сделка в базе данных), а текущий тренд - вверх, а текущий пересечение в сделке. Пересечение
  2. Deal.is_bought () (сделка в базе данных) и текущий тренд вниз, а текущий пересечение в сделке. Пересечение

Мы получаем прибыль от сделки в одном из случаев:

  1. Сделка прибыльная, сделка имеет только 1 ордер, текущий тренд - ВВЕРХ, текущий пересечение - сделка. Скрещивание
  2. Сделка выгодна, сделка имеет только 1 ордер, текущий тренд - FLAT, а текущий переход - в сделке. Пересечение
  3. Сделка выгодна, сделка имеет только 1 ордер, текущий тренд - ВНИЗ, а текущий переход - в сделке. Пересечение
  4. Сделка прибыльная, в сделке более 1 ордера, текущий тренд - ВВЕРХ, текущий переход - в сделке. Пересечение
  5. Сделка выгодна, сделка имеет более 1 ордера, текущий тренд - FLAT, а текущий переход - в сделке. Пересечение
  6. Сделка выгодна, сделка имеет более 1 ордера, текущий тренд - ВНИЗ, а текущий переход - в сделке. Пересечение

Мы просто обновляем сделку, если не достигнуты deal.is_bought () и другие условия

Ответы [ 4 ]

3 голосов
/ 19 мая 2019

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

Вы можете определить интерфейс (вы можете использовать простой базовый класс или абстрактный базовый класс) для захвата этого. Затем добавьте классы, которые реализуют этот интерфейс (наследуют от базового класса) для различных if's -> action .

Вот пример:

class Rule:

    def isMetBy(self, deal):
        pass

    def execute(self, deal):
        pass

class Rule1(Rule):

    def isMetBy(self, deal):
        return deal.current_trend == FLAT_TREND and CROSSED_BOTTOM_ORANGE in deal.crossing

    async def execute(self, deal):
        return await deal.create_order(CROSSED_BOTTOM_ORANGE)

class Rule2(Rule):

    def isMetBy(self, deal):
        return deal.current_trend == DOWN_TREND and CROSSED_BOTTOM in deal.crossing

    async def execute(self, deal):
        return await deal.create_order(CROSSED_BOTTOM)

rules = [Rule1(), Rule2()]

async def check_deal(deal):

    for rule in rules:
        if(rule.isMetBy(deal)):
            return await rule.execute(deal)

Каждое правило содержит оператор if, являющийся вашим кодом. Таким образом, вы можете определить столько правил, сколько вам нужно. Добавление новых - это просто добавление другого класса. Если вы можете, вы назовете свои правила хорошими именами, так что это добавит преимущество или naming ваши правила. Я использовал Rule1 и Rule2, потому что я не знаю, что означают ваши правила. Например, у вас может быть что-то вроде (я только что сделал это для иллюстрации) EnableTrailingForProfitableDealsRule .

В вашем случае у вас есть один , если с несколькими subif's внутри. Вы можете разделить их на разные классы или использовать Composite pattern , чтобы создать rule с subrules .

Вот пример:

class CompositeRule(Rule):
    subRules = []

    def __init(self):
        self.subRules = [Rule1(), Rule2()]

    def isMetBy(self, deal):

        if deal.is_profitable() and len(deal.orders) == 1 :
            for rule in self.subRules:
                if(rule.isMetBy(deal)):
                    return true;

        return false

    async def execute(self, deal);

        for rule in self.subRules:
            if(rule.isMetBy(deal)):
                return await rule.execute(deal)

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

Вот CompositeRule, разбитый на два простых правила:

class Rule1(Rule):

    def isMetBy(self, deal):
        return if deal.is_profitable() and len(deal.orders) == 1 and deal.current_trend == UP_TREND  and CROSSED_TOP in deal.crossing

    async def execute(self, deal):
        return await deal.trailing_enable(CROSSED_TOP)

class Rule2(Rule):

    def isMetBy(self, deal):
        return if deal.is_profitable() and len(deal.orders) == 1 and  deal.current_trend == FLAT_TREND and CROSSED_TOP_GREEN in deal.crossing:

    async def execute(self, deal):
        return await deal.trailing_enable(CROSSED_TOP_GREEN)

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

2 голосов
/ 19 мая 2019

Одной из причин сложности вашего кода является то, что вы обрабатываете объекты процедурным способом: check_deal проверяет атрибуты сделки и принимает решения на их основе, затем вызывая методы сделки. Эта логика принадлежит классу Deal, скажем, в методе Deal.check.

Перемещение логики в класс Deal не делает ее менее сложной, но открывает путь к различным вариантам проектирования.

Например, вместо того, чтобы проверять, является ли Deal новым или купленным или законченным в блоках условного кода, вы можете иметь подклассы NewDeal, BoughtDeal и FinishedDeal, чьи методы check касаются только решения, которые влияют на эти конкретные типы.

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

Другой подход может состоять в том, чтобы моделировать события в жизненном цикле Deal как конечный автомат. Создайте State классы, которые представляют каждую стадию в жизненном цикле и которые знают, какие действия необходимо предпринять, и как решить, каким должно быть следующее состояние (здесь может быть полезна цепочка ответственности). Это может потребовать тщательного моделирования, чтобы предотвратить чрезмерную связь между Deal и State.

Короче говоря, попытайтесь структурировать свою бизнес-логику так, чтобы у вас были классы, которые не должны принимать решения: они просто делают правильные вещи или, по крайней мере, делают правильные вещи с минимальным количеством условного кода.

0 голосов
/ 19 мая 2019

Если вы используете «elif», вы делаете работу своих компьютеров немного легче, потому что тогда он проверяет сначала «если», и если это правда, он игнорирует все «elif» и «еще», которые связаны с этим «если», ноЕсли вы используете «если» в каждом условии (как и вы), ваш компьютер будет проходить через каждое утверждение «если» и проверять его.Я надеюсь, что это имеет смысл.

0 голосов
/ 19 мая 2019

Теперь это работает, но это чертовски ifs.

Ваш if -логический дефект - у вас недоступный код:

if deal.is_bought() and not deal.candle_has_order:
    if deal.current_trend == UP_TREND and CROSSED_BOTTOM_GREEN in deal.crossing:
        return await deal.create_order(CROSSED_BOTTOM_GREEN)
    else:
        return await deal.update_deal()

    # Neither of the following will EVER happen, you already 
    # left the function with the _else_ from above:
    if deal.current_trend == DOWN_TREND and CROSSED_BOTTOM in deal.crossing:
        return await deal.create_order(CROSSED_BOTTOM)
    else:
        return await deal.update_deal()

Я пробовал модели «Стратегия и цепочка ответственности», но моих знаний и опыта недостаточно, чтобы сделать это правильно.И я не уверен, что принял правильное решение.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...