Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions .idea/dataSources.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

35 changes: 35 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Торговый бот на Tinkoff Invest API
Торговый бот реализующий торговлю инструментами (на данном этапе фондами и акциями), основывающий решения о покупке/продаже на основе
<a href="https://en.wikipedia.org/wiki/Relative_strength_index">RSI</a>. Есть возможность добавлять/удалять инструменты, которыми ведется торговля,
ограничивать количество средств, доступных боту для торговли одним инструментом. Управление ботов ведется через ввод команд в консоль. Подробнее о доступных командах ниже.

## Структура проекта
Торговый бот реализован в качестве интерактивного консольного приложения, есть возможность быстрого расширения функционала в виде подключения к базе данных и графического пользовательского интерфейса.
Логически приложение разделено на четыре части:
1. Взаимодействие с пользователем
Получение команд, обработка запросов и исключительных ситуаций
2. Вычислительная
По полученным данным вычисляются коэффициенты (RSI, NVI, PVI; в данной программе реализован только RSI), по ним принимаются решения о купле и продаже
3. Соединительная
Создание потоков и унарных запросов к API для получения и отправки данных, на основе принятых решений и вычисленных индексов
4. Хранение данных
Хранение истории всех сделок, компании с доступными компаниями для торговли и индексами
## Установка и запуск
Установите java 11 и выше
Далее откройте командную строку (в Windows от имени администратора) и перейдите в директорию с jar файлом
Введите java -jar НазваниеФайла.jar
## Начало работы
При запуске бота будет выведен запрос на введение токена. Токен можно сгенерировать на сайте Тинькофф инвестиции в разделе токен.
Далее нужно выбрать номер аккаунта, по которому будет вестись торговля. Затем включается интерактивный режим с возможностью ввода команд.
Первой введите команду help для ознакомления со всеми возможностями бота.

## Доступные команды
<ul>
<li>help - список всех команд
<li>add - Добавление инструмента. Нужно ввести FIGI инструмента, деньги, предоставляемые боту для торговли инструментом, максимальный процент просадки, после которого происходит продажа, процент прибыли, после которого бот начинает продавать инструмент
<li>changeCompany - Изменение параметров для торговли (stop loss, take-profit, free money, etc...)
<li>startTrade - Начать торговать инструментом. Команда add лишь добавляет инструмент в программу, для торговли используйте эту команду
<li>stopTrade - Перестать торговать инструментом
<li>delete - Удалить инструмент из программы
<li>printSchedule - Вывести расписание бирж
<li>exit - Выход из программы. Вся торговля останавливается. На данном этапе при следующем запуске программа не будет помнить торги с предыдущей сессии
Binary file removed out/artifacts/TinkoffBot_jar/TinkoffBot.jar
Binary file not shown.
8 changes: 8 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,24 @@
<artifactId>java-sdk-core</artifactId>
<version>1.0-M8</version>
</dependency>

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.0-alpha7</version>
</dependency>

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>2.0.0-alpha7</version>
</dependency>

<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
<version>3.36.0.3</version>
</dependency>
</dependencies>

<properties>
Expand Down
1 change: 0 additions & 1 deletion src/main/java/Connection/CandleStream.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ public void initialize(DataStreamProcessor processor) throws OutNumberOfReconnec
*/
public void updateSubscription() {
if(!companies.getFigisOfTradingCompanies().isEmpty()) {
System.out.println(companies.getFigisOfTradingCompanies().get(0));
stream.subscribeCandles(companies.getFigisOfTradingCompanies(), SubscriptionInterval.SUBSCRIPTION_INTERVAL_ONE_MINUTE);
}
}
Expand Down
4 changes: 3 additions & 1 deletion src/main/java/Connection/TradeStream.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ public void buyStock(long lots, Quotation price, String figi) throws CompanyNotF
System.out.println("going to buy");
orderId = Double.valueOf(Math.random()).hashCode();

//todo: verification of params
var orderResponse = tradeServ.postOrderSync(
figi,
lots,
Expand All @@ -62,7 +63,7 @@ public void buyStock(long lots, Quotation price, String figi) throws CompanyNotF
if(orderResponse.getExecutionReportStatus().equals(OrderExecutionReportStatus.EXECUTION_REPORT_STATUS_FILL) ||
orderResponse.getExecutionReportStatus().equals(OrderExecutionReportStatus.EXECUTION_REPORT_STATUS_PARTIALLYFILL)){
Company curComp = companies.getByFigi(figi);
System.out.println("������� ����� " + orderResponse.getLotsExecuted() + " �� ���� " +
System.out.println("Buy lot^: " + orderResponse.getLotsExecuted() + " price: " +
orderResponse.getExecutedOrderPrice().toString() + " \n");

curComp.buyShares(
Expand All @@ -85,6 +86,7 @@ public void buyStock(long lots, Quotation price, String figi) throws CompanyNotF
*/
public void sellStock(long lots, Quotation price, String figi, Deal deal) throws CompanyNotFoundException {
System.out.println("Going to sell");
//todo: verification of params
var orderResponse = tradeServ.postOrderSync(
figi,
lots,
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/Data/Company.java
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ public void buyShares(long lotNumber, BigDecimal price, String id) {
}

/**
* change company freemoney, openDeals and shareValue after selling some lots
* change company free money, openDeals and shareValue after selling some lots
* @param deal
* @param lotNumber
* @param price
Expand Down
8 changes: 5 additions & 3 deletions src/main/java/Data/OpenDeals.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
/**
* Class for working with history of openDeals - staff, that bot bought, and should to sell in some cases
* Deal store price of buying, date, price of stopLoss, number of bought lots
* Open deals stores concurrent List, in wich you can add deal, delete deal, print etc.
* Open deals stores concurrent List, in which you can add deal, delete deal, print etc.
* Class-wrapper for collection of deals
*/
public class OpenDeals {
Expand Down Expand Up @@ -50,7 +50,9 @@ public void deletePartly(Deal deal, long numberOfSoldLots) {
addDeal(new Deal(deal.getLotNumber() - numberOfSoldLots,
deal.getPrice(),
deal.getStopPrice(),
deal.getId()));
String.valueOf(Double.valueOf(Math.random()).hashCode())
)
);
}
deleteDeal(deal);
}
Expand All @@ -64,7 +66,7 @@ public List<Deal> getDealsAsList() {
}

/**
* sort opendeals by price
* sort open deals by price
*/
public void sortByPrices() {
this.openDeals.sort(Comparator.comparing(Deal::getPrice));
Expand Down
127 changes: 127 additions & 0 deletions src/main/java/Database/DatabaseConnector.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package Database;

import UI.Console.Console;

import java.sql.*;

public class DatabaseConnector {
private Connection connection;
private final String JDBC_DRIVER = "org.sqlite.JDBC";

public void connect() {
try {
Class.forName(JDBC_DRIVER);
connection = DriverManager.getConnection("jdbc:sqlite:src/main/resources/companies.db");
Console.println("Successful database connection");
} catch (ClassNotFoundException exception) {
Console.printError("Driver cannot find!");
} catch (SQLException exception) {
Console.printError("Database connection error");
}
}

/**
* @param sqlStatement SQL statement to be prepared.
* @param generateKeys Is keys needed to be generated.
* @return Prepared statement.
* @throws SQLException When there's exception inside.
*/
public PreparedStatement getPreparedStatement(String sqlStatement, boolean generateKeys) throws SQLException {
PreparedStatement preparedStatement;
try {
if (connection == null) throw new SQLException();
int autoKey = generateKeys ? Statement.RETURN_GENERATED_KEYS : Statement.NO_GENERATED_KEYS;
preparedStatement = connection.prepareStatement(sqlStatement, autoKey);
return preparedStatement;
} catch (SQLException exception) {
throw new SQLException(exception);
}
}

/**
* Close prepared statement.
* @param sqlStatement SQL statement to be closed.
*/
public void closePreparedStatement(PreparedStatement sqlStatement) {
if (sqlStatement == null) return;

try {
sqlStatement.close();
} catch (SQLException exception) {
Console.printError("It's not possible to close prepared statement");
}
}

/**
* Close connection to database.
*/
public void closeConnection() {
if (connection == null) return;
try {
connection.close();
Console.println("Database connection finished.");
} catch (SQLException exception) {
Console.printError("Database connection error!");
}
}

/**
* Set commit mode of database.
*/
public void setCommitMode() {
try {
if (connection == null) throw new SQLException();
connection.setAutoCommit(false);
} catch (SQLException exception) {
Console.printError("An error occurred while setting the database transaction mode!");
}
}

/**
* Set normal mode of database.
*/
public void setNormalMode() {
try {
if (connection == null) throw new SQLException();
connection.setAutoCommit(true);
} catch (SQLException exception) {
Console.printError("An error occurred while establishing normal database mode!");
}
}

/**
* Commit database status.
*/
public void commit() {
try {
if (connection == null) throw new SQLException();
connection.commit();
} catch (SQLException exception) {
Console.printError("An error occurred while validating the new state of the database!");
}
}

/**
* Roll back database status.
*/
public void rollback() {
try {
if (connection == null) throw new SQLException();
connection.rollback();
} catch (SQLException exception) {
Console.printError("An error occurred while reverting the original state of the database!");
}
}

/**
* Set save point of database.
*/
public void setSavepoint() {
try {
if (connection == null) throw new SQLException();
connection.setSavepoint();
} catch (SQLException exception) {
Console.printError("Database saving error!");
}
}
}
Loading