Сбер: как некрасиво поступить на конкурсе красоты

Пост также можно почитать на habr.com

Всё началось, когда я узнал про конкурс красоты кода от Сбера. Я как раз хотел поучаствовать в каком-нибудь эпичном конкурсе, а тут он мне и подвернулся, тем более что я - тот человек, которому есть что рассказать про красивый код. Я даже целую статью запилил о том, как писать красивый и понятный код. Так что я решил, что в данном случае мои шансы на победу - в отличие от остальных конкурсов - всё же больше нуля. Кроме того, я хотел выступить на конференции PiterPy (спойлер: хрен мне), чтобы рассказать там про красивый код и всё такое, поэтому участие в конкурсе и сравнение результатов было бы классным подспорьем.

Погромирование

🔗

Про конкурс я узнал поздновато, оставалась где-то пара дней, и у меня была установка: не тратить на конкурс слишком много времени. Банально потому что тратить больше пары часов невыгодно - если меня не возьмут в победители, то по крайней мере двух часов мне не жалко. Поэтому я выделил 2 часа и ровно в 1:00 ночи накануне окончания конкурса я начал писать код.

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

Напишите программу для симуляции торговли на финансовых рынках, включая методы для выполнения транзакций покупки и продажи активов и получения текущей стоимости портфеля.

from decimal import Decimal
from enum import Enum

class AssetPrice(Enum):
    # Котировальный список активов.
    LKOH = Decimal(5896)
    SBER = Decimal(250)

Про торговлю на бирже я знаю только одно: картинку "стонкс". Поэтому первым делом я, конечно, добавил её в программу, чтобы казаться уверенным трейдером и человеком, который знает, о чём речь:

@dataclass
class PortfolioSimulator:
    def __post_init__(self):
        self.logo = (Path(__file__).parent / 'logo.txt').read_text()

    def run(self):
        """ Интерактивный режим """
        self.print_greeting()

    def print_greeting(self):
        print(self.logo)
        print('Добро пожаловать в симулятор торговли активами!')
        print('Постарайтесь не разориться в первый день :>')
> python trading-simulation.py

⠀⠀⠀⠀⠀⢀⠤⠐⠒⠀⠀⠀⠒⠒⠤⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⡠⠊⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠑⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⡔⠁⠀⠀⠀⠀⠀⢰⠁⠀⠀⠀⠀⠀⠀⠈⠆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⢰⠀⠀⠀⠀⠀⠀⠀⣾⠀⠀⠔⠒⠢⠀⠀⠀⢼⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⡆⠀⠀⠀⠀⠀⠀⠀⠸⣆⠀⠀⠙⠀⠀⠠⠐⠚⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠇⠀⠀⠀⠀⠀⠀⠀⠀⢻⠀⠀⠀⠀⠀⠀⡄⢠⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡀⠀⠀
⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⣀⣀⡠⡌⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡀⢄⣲⣬⣶⣿⣿⡇⡇⠀
⠀⠀⠆⠀⠀⠀⠀⠀⠀⠀⠘⡆⠀⠀⢀⣀⡀⢠⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⢴⣾⣶⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀
⠀⠀⢸⠀⠀⠀⠀⠠⢄⠀⠀⢣⠀⠀⠑⠒⠂⡌⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠛⢿⣿⣿⣿⣿⣿⣿⣿⡇⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠑⠤⡀⠑⠀⠀⠀⡘⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡀⣡⣿⣿⣿⣿⣿⣿⣿⣇⠀
⠀⠀⢀⡄⠀⠀⠀⠀⠀⠀⠀⠈⢑⠖⠒⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠐⣴⣿⣿⣿⡟⠁⠈⠛⠿⣿⠀
⠀⣰⣿⣿⣄⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⢈⣾⣿⣿⣿⠏⠀⠀⠀⠀⠀⠈⠀
⠈⣿⣿⣿⣿⣷⡤⣀⡀⠀⠀⢀⠎⣦⣄⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣢⣿⣿⣿⡿⠃⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠘⣿⣿⣿⣿⣿⣄⠈⢒⣤⡎⠀⢸⣿⣿⣿⣷⣶⣤⣄⣀⠀⠀⠀⢠⣽⣿⠿⠿⣿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠹⣿⣿⣿⣿⣿⣾⠛⠉⣿⣦⣸⣿⣿⣿⣿⣿⣿⣿⣿⣿⡗⣰⣿⣿⣿⠀⠀⣿⠀⠀⠀⠀⠀⠀⣀⡀⠀⠀
⠀⠀⡰⠋⠉⠉⠉⣿⠉⠀⠀⠉⢹⡿⠋⠉⠉⠉⠛⢿⣿⠉⠉⠋⠉⠉⠻⣿⠀⠀⣿⠞⠉⢉⣿⠚⠉⠉⠉⣿⠀
⠀⠀⢧⠀⠈⠛⠿⣟⢻⠀⠀⣿⣿⠁⠀⣾⣿⣧⠀⠘⣿⠀⠀⣾⣿⠀⠀⣿⠀⠀⠋⠀⢰⣿⣿⡀⠀⠛⠻⣟⠀
⠀⠀⡞⠿⠶⠄⠀⢸⢸⠀⠀⠿⢿⡄⠀⠻⠿⠇⠀⣸⣿⠀⠀⣿⣿⠀⠀⣿⠀⠀⣶⡀⠈⢻⣿⠿⠶⠆⠀⢸⡇
⠀⠀⠧⢤⣤⣤⠴⠋⠈⠦⣤⣤⠼⠙⠦⢤⣤⡤⠶⠋⠹⠤⠤⠿⠿⠤⠤⠿⠤⠤⠿⠳⠤⠤⠽⢤⣤⣤⠴⠟⠀
Добро пожаловать в симулятор торговли активами!
Постарайтесь не разориться в первый день :>

Заодно там видны "красивые" фишечки питона: датаклассы с их __post_init__, чтение файла в одну строчку.

Далее я подумал, что прикольно было бы иметь несколько возможных алгоритмов ценообразования, чтобы можно было попробовать себя на каких-то симулируемых данных, а потом попробовать торговать и в реальных условиях. Поэтому я создал три стратегии:

1 - Стратегия "сегодня мне всё лень": цена акций просто всегда фиксированная. Just for fun.

class AssetPriceHistory(ABC):
    """
    История цен активов.

    Можно переопределять в дочерних классах, чтобы симулировать различные ситуации.
    """

    @abstractmethod
    def __iter__(self) -> Iterator[tuple[date, AssetPrice]]:
        """
        Возвращает историю цен активов - кортежи вида (дата, цены активов).
        """
        ...


@dataclass
class ChillAssetPriceHistory(AssetPriceHistory):
    """
    История цен активов, в которой цены не меняются.

    Когда устали от трейдинга и просто хотите не думать.
    """

    def __iter__(self) -> Iterator[tuple[date, AssetPrice]]:
        today = date.today()
        asset_price = AssetPrice()
        for day in count():
            date_ = today + timedelta(days=day)
            yield date_, asset_price

Тут абстрактный базовый класс, type hints, итераторы и itertools.count(). Красота!

2 - Стратегия "мне повезёт": цена скачет псевдорандомно от -50% до +50% базовой цены. Мастерство трейдинга тут не отточишь, зато можно понять уровень удачливости трейдера, что тоже немаловажно >:)

@dataclass
class ChaosAssetPriceHistory(AssetPriceHistory):
    """
    История цен активов, в которой цены меняются случайным образом.

    Только для самых отчаянных трейдеров.
    """

    # множитель цены актива; 
    # по умолчанию актив может просесть на 50% или 
    # взлететь на 50% за один день, хе-хе
    price_multiplier: tuple[float, float] = (0.5, 1.5)
    seed: int = 42

    def __post_init__(self):
        self.random = Random()
        self.random.seed(self.seed)

    def __iter__(self) -> Iterator[tuple[date, AssetPrice]]:
        today = date.today()
        base_asset_price = AssetPrice()
        for day in count():
            date_ = today + timedelta(days=day)
            if day == 0:
                asset_price = base_asset_price
            else:
                asset_price = AssetPrice(**{
                    field: getattr(base_asset_price, field) * multiplier
                    for field in AssetPrice.__dataclass_fields__.keys()
                    if (multiplier := Decimal(self.random.uniform(*self.price_multiplier)))
                })

            yield date_, asset_price

Наверно, walrus в цикле и dict unpacking - это слишком мудрёно и некрасиво, зато можно в одном выражении задать цены на день. Ну если не понравится жюри - ну штош!

3 - Стратегия "как в реале": цена была взята просто из реальных данных акций Сбера и ещё чего-то за 10 дней - чтобы человек просто мог поторговать и понять, насколько всё плохо бывает.

@dataclass
class RealAssetPriceHistory(AssetPriceHistory):
    """
    Настоящая история цен активов, взятая из исторических данных.

    Без шуток.

    В дальнейшем можно брать по API откуда-нибудь.
    """

    def __iter__(self) -> Iterator[tuple[date, AssetPrice]]:
        yield from (
            (date(2023, 9, 10), AssetPrice(LKOH=Decimal(6669), SBER=Decimal(255))),
            (date(2023, 9, 11), AssetPrice(LKOH=Decimal(6456), SBER=Decimal(256))),
            (date(2023, 9, 12), AssetPrice(LKOH=Decimal(6729), SBER=Decimal(262))),
            (date(2023, 9, 13), AssetPrice(LKOH=Decimal(6610), SBER=Decimal(258))),
            (date(2023, 9, 14), AssetPrice(LKOH=Decimal(6519), SBER=Decimal(260))),
            (date(2023, 9, 15), AssetPrice(LKOH=Decimal(6553), SBER=Decimal(260))),
            (date(2023, 9, 16), AssetPrice(LKOH=Decimal(6527), SBER=Decimal(260))),
            (date(2023, 9, 17), AssetPrice(LKOH=Decimal(6566), SBER=Decimal(263))),
        )

Это, конечно, кустарщина, и в идеале нужно в этом классе получать реальные исторические данные по API из какого-нибудь сервиса, но это не для тестового задания. Поэтому просто забиваем значения ручками. Зато yield from - красиво!

А вот как это встраивается в симулятор торговли:

@dataclass
class PortfolioSimulator:
    history: AssetPriceHistory = field(default_factory=RealAssetPriceHistory)  # dependency injection такое, можно подставлять разные истории и тренироваться на разных данных

    def __post_init__(self):
        self.days = iter(self.history)
        self.next_day()  # получаем первую дату и цены активов
        ...

    def next_day(self):
        """ Закончить день и получить цены нового дня """
        self.current_date, self.current_prices = next(self.days)

Получился такой вот dependency injection (красиво!), чтобы можно было легко применять стратегии в нашем симуляторе. default_factory по умолчанию ставит реальные исторические данные. В дальнейшем представители Сбера могут взять мой код, вставить туда свой исторический класс и сразу пушить во все репозитории, которые у них есть, и деплоить в продакшн. Очень удобно.

Далее осталось написать саму программу, которая бы обрабатывала пользовательский ввод, считала портфель и так далее. Я ничего особо крутого изобретать не стал, просто постарался логически разбить всё на маленькие кусочки, которые были простые для понимания, плюс добавил match-case просто чтобы выпендриться и показать, что "Хоба! Я знаю про match-case!" (его ведь для этого и создали, да?)

Ниже - код симулятора, всё неинтересное я превратил в троеточие или убрал, для любопытных есть репа.

@dataclass
class PortfolioSimulator:
    history: AssetPriceHistory = field(default_factory=RealAssetPriceHistory)  # dependency injection такое, можно подставлять разные истории и тренироваться на разных данных
    cash: Decimal = Decimal(100_000)  # начальный капитал
    assets: defaultdict = field(default_factory=lambda: defaultdict(int))  # сколько активов у нас есть (в начале мы ничего не имеем)

    @property
    def asset_values(self) -> list[tuple[str, int, Decimal]]:
        """ Стоимость активов """
        return [
            (asset, quantity, getattr(self.current_prices, asset) * quantity)
            for asset in self.current_prices.__dataclass_fields__.keys()
            if (quantity := self.assets[asset]) != 0  # не показываем то, чего не имеем
        ]

    @property
    def value(self) -> Decimal:
        """ Стоимость портфеля """
        return self.cash + sum(price for _, _, price in self.asset_values)

    @property
    def profit(self) -> Decimal:
        """ Прибыль """
        return self.value - self.initial_value

    def run(self):
        """ Интерактивный режим """
        self.print_greeting()

        while True:
            self.print_summary()
            try:
                self.user_action()
            except StopGameException:
                break

        self.print_result()

    def print_greeting(self):
        ...

    def print_summary(self):
        ...

    def user_action(self) -> bool:
        """ Выбор действия пользователя """

        match input(dedent("""
            Что вы хотите сделать?
            1. Купить актив
            2. Продать актив
            3. Закончить день
            4. Зафиксировать значения и завершить программу
        """)):
            case "1":
                asset = input("Какой актив вы хотите купить? ")
                amount = input_int("Сколько? ")
                try:
                    self.buy(asset, amount)
                except (WrongAssetName, NotEnoughCash) as exc:
                    print(exc)
                else:
                    print(f"Вы купили {amount} {asset}")
                sleep(1)

            case "2":
                asset = input("Какой актив вы хотите продать? ")
                amount = input_int("Сколько? ")
                try:
                    self.sell(asset, amount)
                except (WrongAssetName, NotEnoughAsset) as exc:
                    print(exc)
                else:
                    print(f"Вы продали {amount} {asset}")
                sleep(1)

            case "3":
                try:
                    self.next_day()
                except StopIteration as exc:
                    raise StopGameException() from exc

            case "4":
                raise StopGameException()

            case _:
                print("Неправильный выбор, попробуйте ещё раз.")

    def print_result(self):
        if (profit := self.profit) > 0:
            print("Stonks! Вы закончили торговлю с прибылью! Отправляю данные о вас в налоговую! }:)")
        elif profit == 0:
            print("Ну вы хоть не разорились, это уже хорошо")
        else:
            print("Not stonks! Вы закончили торговлю в минус! Штош, бывает :[")

Мне кажется, что тут всё чисто и понятно, потому что код читается почти как естественный язык. За исключением def asset_values, конечно - для конкурса можно было бы и развернуть в обычный for цикл, но я что-то не подумал.

Как раз уже было 3:00 ночи, я подумал, что больше ни минуты не потрачу на эту штуку. Я заполнил форму на сайте и отправил решение. Сайт просто сказал мне, что "окей чувак, всё получено", и больше ничего мне не пришло - ни email, никакого подтверждения вообще, как будто меня не существовало вовсе. Я даже до сих пор не уверен, есть я там в их системе или нет. До жути напоминает электронное голосование :]

Ожидание

🔗

Ну и собственно я стал ждать. Было сказано, что победителей объявят через несколько дней. В заветный день я полез на сайт, чтобы поскорее узнать результаты - но даже и на следующее число на сайте ничего не обновилось. Я решил, что Сбер завалило красивым кодом, и они в спешке всё просматривают и не успевают. Но потом я читаю какой-то паблик в телеграме и внезапно вижу запись о том, что Сбер уже наградил победителей этого конкурса на своей SmartDev конференции. Так, ладно, сейчас посмотрим...

Я выпал в осадок, потому что на самом сайте никаких результатов не было, и почему вообще людей награждают на какой-то конференции, и почему мне никто не написал, что я дебил и что я плохо всё решил? В общем, это была какая-то мутная тема. Меня это, конечно, напрягло, и люди, которые читают Хабр, в комментариях к статье написали:

  • Каком способом можно будет узнать результаты конкурса? Написано в правилах, что результаты будут сегодня, но не написано, каким способом их оглашают) Заранее спасибо)
  • А где-то можно посмотреть решения победителей по направлению "Frontend"?
  • Результаты опубликованы на день позже обозначенного срока, причем никакого уведомления мне как участнику не пришло, не обозначены критерии оценок, сколько баллов набрала моя работа, вывешен просто список фио "победителей". С кодом лидеров хоть бы дали возможность ознакомиться. Мутно как-то все.
  • да, организация на 2 с плюсом. Никаких рассылок на почту. Откуда я должен знать, что оглашение результатов конкурса будет на какой-то конференции SmartDev? Этого нет в полных правилах конкурса.

Потом Сбер таки сделал жест и написал список победителей в формате {first_name} {last_name}, {city}: Сделали по красоте: победители «Конкурса красоты кода»

Ну уж нет, Сберушка! Результаты должны быть честными, должны учить нас чему-то. Покажите мне победителя, расскажите, какой у него опыт, где он работает, как он научился писать по красоте (чтобы я так же смог), и главное, что я спрашиваю на любых разборках: покажите мне код!

Я, конечно, написал Оксимирону (псевдоним - Артём Фатхуллин), который награждал победителей, с вопросом "как мне связаться с победителями и посмотреть код", но он прочитал и ничего не ответил.

Реальность

🔗

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

На самом деле я не мастер по OSINT, я даже сам себя в интернете найти не могу :D Но я постарался, честное слово, и я нашёл только двоих питонистов: мистера "Изящный код" aka Ивана Звонарёва из Ижевска, и мисс "Изящный код" aka Арину Жук из Москвы.

Арина любезно поделилась со мной своим решением по секции Data Science. Я не эксперт по ML, но мне оно понравилось - всё разложено по полочкам, легко читается, задачу решает. Вообще без претензий, по моему дилетантскому мнению - приз заслужен. Почему Сбер это не выложил - я хз.

Код Ивана я нашёл на гитхабе, позже в личной переписке он подтвердил, что он - это он, и это действительно тот код, что он отправил на конкурс и победил. Вот и сам код: https://github.com/Royal00Blood/Sber_task

Мне кажется, что он не самый красивый, и вот почему - по пунктам.

Это "интерфейс", но не в программистском понимании, а как бы "интерфейс взаимодействия с пользователем":

def interface():
    exchange = FExchange()
    action = None

    print(" 1 - Sell \n",
          "2 - Buy \n",
          "3 - Show the value of the financial portfolio ",
          "4 - exit")

    while 1:

        try:
            action = int(input("Enter 1, 2, 3 or 4: "))
        except ValueError:
            print("Sorry! You enter no number!")

        if action == 1:
            exchange.sell_shares()

        elif action == 2:
            exchange.buy_shares()

        elif action == 3:
            exchange.e_cash_show()

        elif action == 4:
            exit()

Тут 3 момента:

  • Ожидается, что весь интерфейс будет реализован в этом модуле (он же называется Interface.py!), однако на самом деле ввод-вывод размазан по модулям. Это некрасиво.
  • action = None нужно, потому что даже при плохом вводе содержимое цикла продолжает выполняться вместо того, чтобы сделать continue и попросить заново ввести корректное значение
  • При выборе 4 программа просто бескомпромиссно завершается. Это некрасиво, exit() лучше делать на "верхних" уровнях (где-нибудь в main), а из функций лучше просто выходить: elif action == 4: break

Ну ладно, что там в FExchange?

from AssetPrice import AssetPrice


class FExchange:
    """
    This is the main class that performs
    the function of simulating the processes
    of selling and buying shares on a financial exchange.
    """
    def __init__(self):
        self.__e_cash = 0  # The value of the financial portfolio
        self.__fin_p = {a.name: 0 for a in AssetPrice}  # Stocks in the financial portfolio
        self.__f_abil = False  # The possibility of selling something

    def e_cash_show(self):
        """
        :return:Displays the value of all shares in the portfolio
        """
        self.__capital_calcul()
        print(self.__e_cash)

    def buy_shares(self):
        """
        The function performs the process of buying shares from the available.
        :return:
        """
        for i in AssetPrice:
            print(f"-----------> You have  {i.name} shares of {self.__fin_p[i.name]} ")
        print("You can to buy :")
        for x in AssetPrice:
            print(f"-----------> Company: {x.name}, coast: {x.value}")
        self.__action_shares()

    def sell_shares(self):
        """
        The function performs the process of selling shares from a financial portfolio.
        :return:
        """
        for i in AssetPrice:
            if not self.__f_abil:
                if self.__fin_p[i.name] > 0:
                    self.__f_abil = True
                    print(f"You have  {i.name} shares of {self.__fin_p[i.name]} ")
        if not self.__f_abil:
            print("You don't have any shares")
        else:
            self.__action_shares(incr=False)

    def __action_shares(self, incr=True):
        """
        Function for changing the values of stocks in the financial portfolio.
        :param incr: А logical variable that determines the increase or decrease of a block of shares.
        :return:
        """
        while 1:
            n_promotion = input('Enter name of the promotion: ')
            if n_promotion in self.__fin_p:
                break
            else:
                print("Enter again. You have  mistake. (Example: SBER)")
        while 1:
            try:
                c_promotion = int(input('Enter the number of shares to buy: '))
            except ValueError:
                print("Enter again")
            else:
                break
        if incr:
            self.__fin_p[n_promotion] += c_promotion
        else:
            self.__fin_p[n_promotion] -= c_promotion

    def __capital_calcul(self):
        """
        The function calculates the sum of all the shares in the financial portfolio.
        :return: Current value of the financial portfolio.
        """
        for i in AssetPrice:
            self.__e_cash += self.__fin_p[i.name] * i.value
  • Почему __f_abil, __fin_p, __e_cash называются так, как называются? Как же читаемость? __e_cash - это цифровой рубль?
  • for i in AssetPrice: - почему i, а не asset?
  • Кажется, понятие "деньги на руках" в этом коде отсутствует в принципе, поэтому в __action_shares нет никакой проверки на лимиты. Кому нужно 100000000000000000 акций сбера? Хоба!
Enter 1, 2, 3 or 4: 2
-----------> You have  LKOH shares of 0 
-----------> You have  SBER shares of 4 
You can to buy :
-----------> Company: LKOH, coast: 5896
-----------> Company: SBER, coast: 250  <--- хочу вот это побережье
Enter name of the promotion: SBER
Enter the number of shares to buy: 100000000000000000
Enter 1, 2, 3 or 4: 3
25000000000000004500   <---- мама, я богат!
  • В __capital_calcul есть side effect, а они очень опасны! Будьте осторожны, а то вдруг при выводе портфолио на экран оно будет само увеличиваться каждый раз? Стоп, что?..
Enter 1, 2, 3 or 4: 3  <-- покажи стоимость портфолио
1500
Enter 1, 2, 3 or 4: 3
2500
Enter 1, 2, 3 or 4: 3
3500
  • __action_shares принимает параметр incr=True/False, типа "купить или продать", но код один и тот же, поэтому при продаже спрашивается, что вы хотите купить:
Enter 1, 2, 3 or 4: 1  <--- продать!
You have  SBER shares of 100000000000000004 
Enter name of the promotion: LKOH
Enter the number of shares to buy: 9999  <--- что хотите купить?
  • Да и вообще продавать можно не то, что покупал, какая нафиг разница! Все эти акции-шмакции только всё усложняют.

Я уже приноровился к местным правилам, поэтому вот моя рабочая стратегия, которая приведёт к успеху.

Сначала у нас ничего нет, ничего продать не можем:

> python main.py
 1 - Sell 
 2 - Buy 
 3 - Show the value of the financial portfolio  4 - exit
Enter 1, 2, 3 or 4: 3
0
Enter 1, 2, 3 or 4: 3
0
Enter 1, 2, 3 or 4: 1
You don't have any shares

Покупаем сбер на всю котлету:

Enter 1, 2, 3 or 4: 2
-----------> You have  LKOH shares of 0 
-----------> You have  SBER shares of 0 
You can to buy :
-----------> Company: LKOH, coast: 5896
-----------> Company: SBER, coast: 250
Enter name of the promotion: SBER
Enter the number of shares to buy: 100

Потом проверяем баланс пару раз, чтобы стало побольше:

Enter 1, 2, 3 or 4: 3
25000
Enter 1, 2, 3 or 4: 3
50000

У меня, конечно, есть только 100 акций Сбера, но Лукойл мне так не нравится, что продам его, чтоб было прям отрицательное число акций:

Enter 1, 2, 3 or 4: 1
You have  SBER shares of 100 
Enter name of the promotion: LKOH
Enter the number of shares to buy: 100
Enter 1, 2, 3 or 4: 3
-514600

Блин, теперь баланс отрицательный! Попробую "отыграться" проверенной схемой - множественной проверкой баланса:

Enter 1, 2, 3 or 4: 3
-514600
Enter 1, 2, 3 or 4: 3
-1079200
Enter 1, 2, 3 or 4: 3
-1643800
Enter 1, 2, 3 or 4: 3
-2208400

Чёрт, теперь баланс уменьшается! У меня 100 Сбера, но попробую продать миллион:

Enter 1, 2, 3 or 4: 1
Enter name of the promotion: SBER
Enter the number of shares to buy: 1000000  <--- это на самом деле продажа
Enter 1, 2, 3 or 4: 3
-252773000

Ладно, кажется, машина меня переиграла. Я должен 252 миллиона. Не буду усугублять.

И заметьте, насколько я плохой трейдер: все эти миллионы я просадил за один день! Хорошо, что в этом симуляторе только один день и есть (то есть цены вообще не меняются) - иначе всё было бы ещё хуже...

И чо

🔗

Все, кто участвовал в коде по питону - теперь вы знаете, как надо было писать, чтобы победить.

Рецензия от погромиста:

  • Это не плохой код, но и не красивый
  • Этот код не решает задачу (где симуляция изменения цен?)
  • Этот код работает с критическими ошибками

Пожалуйста, заметьте, что я тут совершенно не в претензии к Ивану. Мы с ним поговорили немного в телеграме, он самоучка, грызёт Питон, и у меня к таким людям исключительно чувство уважения.

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

Давай, Сбер, скажи, например, что это я ошибся, и это другой Иван Звонарёв из другого Ижевска выложил решение с конкурса и не отрицал, что он победитель. Или что ты ошибся только с одним участником, а у других участников всё как надо. А код решений вы не успели выложить. А Артём Фатхуллин всегда только читает, но ничего не отвечает, такой он человек. Скажи всё, что ты должен сказать в таких случаях - мы все всё понимаем.

Да, я не открыл Америку. Просто в очередной раз убедился.



Новое