linksplatform / Bot

VK bot, GitHub bot, Discord bot, Trader bot
The Unlicense
30 stars 32 forks source link

Trader: Simpliest possible trading bot, that uses associative data storage (Deep or Doublets) #103

Open Konard opened 2 years ago

Konard commented 2 years ago

8500 RUB reward can be claimed by anyone who completes this task.

This bot should be able to trade via simulation or real API.

Simulation API example: https://www.lean.io/ (any other realistic open-source stock market simulator can be used) Real API example: https://tinkoffcreditsystems.github.io/invest-openapi/

Trading target ETF: https://www.tinkoff.ru/invest/etfs/TRUR/

Strategy goal: perform at least 1% better per year than historical average of this ETF.

Possible ideas for strategy to replicate: https://www.tinkoff.ru/invest/social/profile/Alealan/b700fb9d-e555-48be-8893-d592c6b86bec image

Trading API and Strategies should be replaceable and configurable.

All configuration should be implemented in Links Notation. All storage should be implemented in Deep or/and Doublets.

Konard commented 2 years ago

ink

This one of the simplest trading strategies, I have found. We buy at any price, sell at x% higher, wait to drop x% back, repeat.

Konard commented 2 years ago

https://www.tinkoff.ru/invest/social/profile/konard/a83474a7-312a-4469-80b2-fa3d07286037

https://www.tinkoff.ru/invest/social/profile/konard/aeedbb2b-fb7d-425e-8516-778ca7377ce7

https://www.tinkoff.ru/invest/social/profile/konard/69859deb-904a-47db-bb2f-ee07ee378fb7

https://www.tinkoff.ru/invest/social/profile/konard/12251740-bce3-46ae-b296-edb7cf58694f

https://github.com/Tinkoff/investAPI/ https://github.com/Tinkoff/invest-api-csharp-sdk/tree/main/Tinkoff.InvestApi.Sample

function actionBuyEtf(string $ticker, int $lots): void
{
    $token = '<token>';
    $account_id = '<account>';

    try {
        $tinkoff_account = TinkoffClientsFactory::create($token);

        echo 'Ищем ETF инструмент' . PHP_EOL;

        $instruments_request = new InstrumentsRequest();
        $instruments_request->setInstrumentStatus(InstrumentStatus:🎸NT_STATUS_ALL);

        /** @var EtfsResponse $response */
        list($response, $status) = $tinkoff_account->instrumentsServiceClient->Etfs($instruments_request)
            ->wait();

        /** @var Instrument[] $instruments_dict */
        $instruments_dict = $response->getInstruments();

        /**
         * Находим в справочнике (коль он у нас весь есть) нужный нам инструмент
         */
        foreach ($instruments_dict as $instrument) {
            if ($instrument->getTicker() === $ticker) {
                $target_instrument = $instrument;

                break;
            }
        }

        if (empty($target_instrument)) {
            echo 'Инструмент не найден' . PHP_EOL;

            return;
        }

        echo 'Инструмент найден. Попытаемся купить ' . $lots . ' лотов' . PHP_EOL;

        if ($target_instrument->getBuyAvailableFlag()) {
            echo 'Покупка доступна' . PHP_EOL;
        } else {
            echo 'Покупка доступна' . PHP_EOL;

            return;
        }

        echo 'Получаем стакан' . PHP_EOL;

        $orderbook_request = new GetOrderBookRequest();
        $orderbook_request->setDepth(5);
        $orderbook_request->setFigi($target_instrument->getFigi());

        /** @var GetOrderBookResponse $response */
        list($response, $status) = $tinkoff_account->marketDataServiceClient->GetOrderBook($orderbook_request)->wait();

        if (!$response) {
            echo 'Ошибка получения стакана заявок' . PHP_EOL;
        }

        /** @var Order[] $bids */
        /** @var Order[] $asks */
        $bids = $response->getBids();
        $asks = $response->getAsks();

        $top_bid_price = $bids[0]->getPrice();

        $post_order_request = new PostOrderRequest();
        $post_order_request->setFigi($target_instrument->getFigi());
        $post_order_request->setQuantity($lots);
        $post_order_request->setPrice($top_bid_price);
        $post_order_request->setDirection(OrderDirection::ORDER_DIRECTION_BUY);
        $post_order_request->setAccountId($account_id);
        $post_order_request->setOrderType(OrderType::ORDER_TYPE_LIMIT);

        $order_id = Yii::$app->security->generateRandomLettersNumbers(32);

        $post_order_request->setOrderId($order_id);

        /** @var PostOrderResponse $response */
        list($response, $status) = $tinkoff_account->ordersServiceClient->PostOrder($post_order_request)->wait();

        if (!$response) {
            echo 'Ошибка отправки торговой заявки' . PHP_EOL;

            return;
        }

        echo 'Заявка с идентификатором ' . $response->getOrderId() . ' отправлена' . PHP_EOL;
    } catch (Throwable $e) {
        echo $e->getMessage() . PHP_EOL;
        echo $e->getTraceAsString() . PHP_EOL;

        Log::error('Error: ' . $e->getMessage());
    }
}

C1EA5B92-4572-4381-9F1B-5CF5716B14DE

7D8E5EB4-2525-4239-A91A-75D39FA3BF72

Konard commented 2 years ago

https://www.tinkoff.ru/invest/social/profile/konard/57fd7481-8568-4c38-9c2d-6aab3f0893a0/

Пример расчётов, пока удалось сделать 0.8% в день на этой стратегии, все сделки безопасные (ни одной в минус):

https://docs.google.com/spreadsheets/d/17qJGS256Xel5CNnCU6gB6D9vvIlJkEfUVoKMRSGaq38/edit?usp=sharing

ACD20AB4-A7E4-44E8-8407-4567538D8973

EFF65E86-6284-4589-95B7-CCEC63B19156

Konard commented 2 years ago

Each day should be closed in half ETF, half cash for optimal performance. All sell bids are not movable. All buy bids are movable (should be moved up when empty slots appear). Strategy can have 2*N bids: +1..+N (sell) and -1..-N (buy). Each day should start with placing N bids for sale and N bids for buy. Works best on non-volatile ETFs or currencies. Then trade is available with no commission, this is the best choice.