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

в 6:15, , рубрики: код показал быстро, конкурс красоты, Сбер, Сбербанк, чзх
Сбер. Как некрасиво поступить на конкурсе красоты - 1

Хабр - не жалобная книга, я знаю. Но тут история про код, с примерами, разбором антипаттернов и всё такое, поэтому я рискну.

Всё началось, когда я узнал про конкурс красоты кода от Сбера. Я как раз хотел поучаствовать в каком-нибудь эпичном конкурсе, а тут он мне и подвернулся, тем более что я - тот человек, которому есть что рассказать про красивый код. Я даже целую статью запилил о том, как писать красивый и понятный код. Так что я решил, что в данном случае мои шансы на победу - в отличие от остальных конкурсов - всё же больше нуля. Кроме того, я хотел выступить на конференции 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 миллиона. Не буду усугублять.

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

И чо

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

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

  • Это не плохой код, но и не красивый

  • Этот код не решает задачу (где симуляция изменения цен?)

  • Этот код работает с критическими ошибками

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

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

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

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


Подписывайтесь, котятки: Блог погромиста

Другие мои статьи тут: pogrom.dev

Автор:
kesn

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js