├── rabbi.png ├── resources ├── qml │ ├── CommonLibs │ │ ├── qmldir │ │ └── common.js │ ├── BaseElements │ │ ├── ListViewHeaderStyled.qml │ │ ├── HeaderLabel.qml │ │ ├── ListViewDelegateStyled.qml │ │ ├── SearchField.qml │ │ ├── Separator.qml │ │ ├── TabBarIcon.qml │ │ ├── ToolBarIcon.qml │ │ ├── EmptyLabel.qml │ │ ├── PasswordField.qml │ │ ├── TableToolButton.qml │ │ ├── ListViewStyled.qml │ │ ├── qmldir │ │ ├── DoubleSpinBox8Prec.qml │ │ ├── DoubleSpinBox2Prec.qml │ │ └── BusyIndicatorStyled.qml │ ├── ModelViews │ │ ├── qmldir │ │ ├── OrdersListView.qml │ │ └── OrdersView.qml │ ├── ExchangePageSection.qml │ ├── Emulation.qml │ └── TopBar.qml ├── ui_icons │ ├── droid.ico │ ├── droid.png │ ├── rabbi.ico │ ├── left-arrow.svg │ ├── right-arrow.svg │ ├── down-arrow.svg │ ├── folder.svg │ ├── smartphone.svg │ ├── up-arrow.svg │ ├── cursor.svg │ ├── bookmark.svg │ ├── cancel.svg │ ├── monitor.svg │ ├── settings-2.svg │ ├── search.svg │ ├── layer.svg │ ├── refresh.svg │ ├── remove.svg │ ├── video-camera.svg │ ├── prohibition.svg │ ├── push-pin.svg │ ├── headphones.svg │ ├── unlock.svg │ ├── credit-card.svg │ ├── heart.svg │ ├── moon.svg │ ├── forward.svg │ ├── rewind.svg │ ├── clock.svg │ ├── menu.svg │ ├── profile.svg │ ├── magnet.svg │ ├── video.svg │ ├── lightning.svg │ ├── left-arrow-1.svg │ ├── play-button.svg │ ├── shield.svg │ ├── add.svg │ ├── right-arrow-1.svg │ ├── bluetooth.svg │ ├── edit.svg │ ├── info.svg │ ├── cursor-1.svg │ ├── power.svg │ ├── switch.svg │ ├── download.svg │ ├── zoom-out.svg │ ├── pause.svg │ ├── bag.svg │ ├── volume.svg │ ├── map.svg │ ├── cloud.svg │ ├── cancel-1.svg │ ├── hourglass.svg │ ├── visible.svg │ ├── cut.svg │ ├── export.svg │ ├── paint.svg │ ├── battery.svg │ ├── favorite.svg │ ├── home.svg │ ├── cutlery.svg │ ├── speech-bubble.svg │ ├── briefcase.svg │ ├── correct.svg │ ├── garbage.svg │ ├── printer.svg │ ├── zoom.svg │ ├── voice-recorder.svg │ ├── clip.svg │ ├── bell.svg │ ├── screen.svg │ ├── tag.svg │ ├── share.svg │ ├── waiting.svg │ ├── target.svg │ ├── shutter.svg │ ├── file.svg │ ├── gamepad.svg │ ├── placeholder.svg │ ├── timer.svg │ ├── calendar.svg │ ├── padnote.svg │ ├── layout.svg │ ├── logout.svg │ ├── photo-camera.svg │ ├── gallery.svg │ ├── presentation.svg │ ├── shopping-cart.svg │ ├── upload.svg │ ├── stats.svg │ ├── settings.svg │ ├── link.svg │ ├── envelope.svg │ ├── wifi.svg │ ├── speedometer.svg │ ├── puzzle.svg │ ├── rocket-launch.svg │ ├── sun.svg │ ├── settings-1.svg │ ├── store.svg │ ├── bitcoin.svg │ ├── clock-1.svg │ └── big-database.svg ├── fonts │ └── Montserrat-Regular.ttf ├── default.cfg └── exchange_logos │ └── binance.svg ├── enter_account.png ├── share ├── deploy │ └── linux │ │ └── run.sh └── sql │ ├── dbCryptoShloma.sqlite │ └── dbCryptoShloma.sqlite.sql ├── .gitmodules ├── installer ├── resources │ └── cryptoshloma.qrc ├── packages │ └── cryptoshloma │ │ └── meta │ │ ├── package.xml │ │ └── installscript.qs ├── installer.pro └── config │ └── config.xml ├── src ├── common │ ├── stacktrace.h │ ├── encrypt.h │ ├── enums.h │ ├── constants.h │ ├── utils.h │ └── settings_manager.h ├── models │ ├── trading_pairs_proxy_model.h │ ├── spread_markets_proxy_model.h │ ├── volume_markets_proxy_model.h │ ├── buy_orders_proxy_model.h │ ├── sell_orders_proxy_model.h │ ├── balances_proxy_model.h │ ├── market_orders_proxy_model.h │ ├── trading_pairs_proxy_model.cpp │ ├── open_orders_proxy_model.h │ ├── market_orders_proxy_model.cpp │ ├── balances_model.h │ ├── open_orders_model.h │ ├── trade_history_proxy_model.h │ ├── market_orders_model.h │ ├── trade_history_model.h │ ├── markets_model.h │ ├── buy_orders_proxy_model.cpp │ ├── sell_orders_proxy_model.cpp │ ├── balances_proxy_model.cpp │ ├── spread_markets_proxy_model.cpp │ └── volume_markets_proxy_model.cpp ├── algo │ ├── hanukkah │ │ ├── zero_step.h │ │ ├── step.h │ │ └── i_step.h │ ├── trend_searcher.h │ ├── i_trade_algo.h │ ├── i_analytics_algo.h │ ├── i_analytics_algo.cpp │ ├── hanukkah.h │ ├── spreder.h │ └── i_trade_algo.cpp ├── boilerplate │ ├── base_entry.h │ └── singly_linked_stack.h ├── api │ ├── i_exchange_api_entry.h │ ├── binance_api_controller.h │ ├── temporary_orders_table.h │ └── i_exchange_api_controller.h ├── cryptoshloma.h ├── crud.h ├── storage │ ├── balances_storage.h │ ├── trade_history_storage.h │ ├── market_orders_storage.h │ ├── open_orders_storage.h │ ├── markets_storage.h │ ├── trade_history_storage.cpp │ └── market_orders_storage.cpp ├── emulator.h ├── network │ ├── http_client.h │ └── websocket_client.h └── base │ └── currency.cpp ├── launcher ├── main.cpp ├── ProcessObserver.h └── CMakeLists.txt ├── include ├── Models └── init_logger.h ├── .github ├── workflows │ ├── install_qt.yml │ └── cmake.yml └── ISSUE_TEMPLATE │ └── bug_report.md ├── .gitignore └── cmake ├── utils.cmake ├── version.in ├── VersionResource.rc └── stacktrace.cmake /rabbi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/man-k28/CryptoShloma/HEAD/rabbi.png -------------------------------------------------------------------------------- /resources/qml/CommonLibs/qmldir: -------------------------------------------------------------------------------- 1 | module CommonLibs 2 | CommonLibs 1.0 common.js 3 | -------------------------------------------------------------------------------- /enter_account.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/man-k28/CryptoShloma/HEAD/enter_account.png -------------------------------------------------------------------------------- /share/deploy/linux/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | export LD_LIBRARY_PATH=$(pwd)/framework/lib 3 | ./Launcher $1 4 | -------------------------------------------------------------------------------- /resources/ui_icons/droid.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/man-k28/CryptoShloma/HEAD/resources/ui_icons/droid.ico -------------------------------------------------------------------------------- /resources/ui_icons/droid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/man-k28/CryptoShloma/HEAD/resources/ui_icons/droid.png -------------------------------------------------------------------------------- /resources/ui_icons/rabbi.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/man-k28/CryptoShloma/HEAD/resources/ui_icons/rabbi.ico -------------------------------------------------------------------------------- /share/sql/dbCryptoShloma.sqlite: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/man-k28/CryptoShloma/HEAD/share/sql/dbCryptoShloma.sqlite -------------------------------------------------------------------------------- /resources/fonts/Montserrat-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/man-k28/CryptoShloma/HEAD/resources/fonts/Montserrat-Regular.ttf -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "share/libs/CuteLogger"] 2 | path = share/libs/CuteLogger 3 | url = https://github.com/man-k28/CuteLogger.git 4 | -------------------------------------------------------------------------------- /resources/qml/BaseElements/ListViewHeaderStyled.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | 3 | Rectangle { 4 | color: "#303030" //"#252324" : "#322f31" 5 | height: 25 6 | z: 2 7 | } 8 | -------------------------------------------------------------------------------- /resources/qml/BaseElements/HeaderLabel.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | 3 | EmptyLabel { 4 | font.bold: true 5 | padding: 5 6 | horizontalAlignment: Text.AlignHCenter 7 | text: "" 8 | } 9 | -------------------------------------------------------------------------------- /resources/qml/BaseElements/ListViewDelegateStyled.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | 3 | Rectangle { 4 | color: index % 2 == 0 ? "#252324" : "#303030" 5 | width: view.width 6 | height: 15 7 | } 8 | -------------------------------------------------------------------------------- /resources/qml/BaseElements/SearchField.qml: -------------------------------------------------------------------------------- 1 | import QtQuick.Controls 2.12 2 | import QtQuick.Controls.Material 2.12 3 | 4 | TextField { 5 | placeholderText: qsTr("Filter") 6 | selectByMouse: true 7 | } 8 | -------------------------------------------------------------------------------- /resources/qml/BaseElements/Separator.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | import QtQuick.Controls.Material 2.12 3 | 4 | Rectangle { 5 | color: Material.color(Material.Grey) 6 | width: parent.width 7 | height: 1 8 | } 9 | -------------------------------------------------------------------------------- /installer/resources/cryptoshloma.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | ../../resources/ui_icons/rabbi.ico 4 | ../../resources/ui_icons/rabbi.svg 5 | 6 | 7 | -------------------------------------------------------------------------------- /resources/qml/BaseElements/TabBarIcon.qml: -------------------------------------------------------------------------------- 1 | import QtQuick.Controls 2.12 2 | 3 | TabButton { 4 | ToolTip.timeout: 2000 5 | ToolTip.visible: hovered 6 | display: AbstractButton.IconOnly 7 | antialiasing: true 8 | } 9 | -------------------------------------------------------------------------------- /resources/qml/BaseElements/ToolBarIcon.qml: -------------------------------------------------------------------------------- 1 | import QtQuick.Controls 2.12 2 | import QtQuick.Controls.Material 2.12 3 | 4 | ToolButton { 5 | antialiasing: true 6 | ToolTip.timeout: 2000 7 | ToolTip.visible: hovered 8 | } 9 | -------------------------------------------------------------------------------- /resources/qml/BaseElements/EmptyLabel.qml: -------------------------------------------------------------------------------- 1 | import QtQuick.Controls 2.12 2 | import QtQuick.Controls.Material 2.12 3 | 4 | Label { 5 | text: qsTr("NaN") 6 | font.pixelSize: 10 7 | font.family: "Montserrat" 8 | color: "#fff" 9 | } 10 | -------------------------------------------------------------------------------- /resources/qml/BaseElements/PasswordField.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | import QtQuick.Controls 2.12 3 | import QtQuick.Controls.Material 2.12 4 | 5 | TextField { 6 | focus: true 7 | echoMode: TextInput.Password 8 | selectByMouse: true 9 | ToolTip.timeout: 3000 10 | ToolTip.visible: hovered 11 | } 12 | -------------------------------------------------------------------------------- /installer/packages/cryptoshloma/meta/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | CryptoShloma 4 | CryptoShloma 5 | 0.3 6 | 2020-12-27 7 | true 8 | 9 | 10 | -------------------------------------------------------------------------------- /resources/qml/BaseElements/TableToolButton.qml: -------------------------------------------------------------------------------- 1 | import QtQuick.Controls 2.12 2 | import QtQuick.Controls.Material 2.12 3 | 4 | ToolButton { 5 | padding: 0 6 | spacing: 0 7 | width: 14 8 | height: 14 9 | icon.color: Material.color(Material.Green) 10 | antialiasing: true 11 | hoverEnabled: true 12 | ToolTip.timeout: 2000 13 | ToolTip.visible: hovered 14 | } 15 | -------------------------------------------------------------------------------- /src/common/stacktrace.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #ifdef Q_OS_LINUX 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | void linuxSignalStacktraceHandler(int signalNumber, siginfo_t *, void * secret ) noexcept; 14 | 15 | #endif 16 | 17 | void installSignalHandlers() noexcept; 18 | -------------------------------------------------------------------------------- /resources/ui_icons/left-arrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/common/encrypt.h: -------------------------------------------------------------------------------- 1 | #ifndef ENCRYPT_H 2 | #define ENCRYPT_H 3 | 4 | #include 5 | 6 | class Encrypt 7 | { 8 | public: 9 | Encrypt() = default; 10 | static QByteArray sha256(const QByteArray& text); 11 | static QByteArray decrypt(const QByteArray& data, const QByteArray& password); 12 | static QByteArray encrypt(const QByteArray& data, const QByteArray& password); 13 | }; 14 | 15 | #endif // ENCRYPT_H 16 | -------------------------------------------------------------------------------- /resources/ui_icons/right-arrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /resources/ui_icons/down-arrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /resources/qml/CommonLibs/common.js: -------------------------------------------------------------------------------- 1 | function toDouble(styledData) { 2 | if ( styledData !== undefined && styledData !== null) { 3 | if ( typeof styledData === 'number' ) { 4 | if ( (Number(styledData) === styledData && styledData % 1 === 0) ) 5 | return styledData 6 | else 7 | return styledData.toFixed(8) 8 | } else 9 | return styledData 10 | 11 | } else 12 | return "" 13 | } 14 | -------------------------------------------------------------------------------- /src/common/enums.h: -------------------------------------------------------------------------------- 1 | #ifndef ENUMS_H 2 | #define ENUMS_H 3 | #include 4 | 5 | class Exchange final { 6 | Q_GADGET 7 | public: 8 | Exchange() = delete; 9 | enum class Type : quint8 10 | { 11 | Unknown = 1, 12 | Binance 13 | }; 14 | Q_ENUM(Type) 15 | }; 16 | 17 | enum AlgoType { 18 | HanukkahAlgo, 19 | SprederAlgo 20 | }; 21 | 22 | enum AnalyticsAlgoType { 23 | TrendSearcherAlgo 24 | }; 25 | 26 | #endif // ENUMS_H 27 | -------------------------------------------------------------------------------- /resources/ui_icons/folder.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/models/trading_pairs_proxy_model.h: -------------------------------------------------------------------------------- 1 | #ifndef TRADINGPAIRSPROXYMODEL_H 2 | #define TRADINGPAIRSPROXYMODEL_H 3 | #include 4 | 5 | class TradingPairsProxyModel final : public QSortFilterProxyModel 6 | { 7 | Q_OBJECT 8 | public: 9 | explicit TradingPairsProxyModel(QObject *parent = nullptr) noexcept; 10 | protected: 11 | bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const noexcept override; 12 | }; 13 | 14 | #endif // TRADINGPAIRSPROXYMODEL_H 15 | -------------------------------------------------------------------------------- /resources/qml/ModelViews/qmldir: -------------------------------------------------------------------------------- 1 | module ModelViews 2 | BalanceView 1.0 BalanceView.qml 3 | OpenOrdersView 1.0 OpenOrdersView.qml 4 | OrdersListView 1.0 OrdersListView.qml 5 | OrdersView 1.0 OrdersView.qml 6 | SpreadMarketsView 1.0 SpreadMarketsView.qml 7 | TradePairPopup 1.0 TradePairPopup.qml 8 | TradingHistorySelectedPairView 1.0 TradingHistorySelectedPairView.qml 9 | TradingHistoryView 1.0 TradingHistoryView.qml 10 | TradingPairsView 1.0 TradingPairsView.qml 11 | VolumeMarketsView 1.0 VolumeMarketsView.qml 12 | -------------------------------------------------------------------------------- /resources/ui_icons/smartphone.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /resources/ui_icons/up-arrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/common/constants.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | static const QString CONST_BTC{QStringLiteral("BTC")}; 4 | static const qint32 CONST_BTC_ID{1}; 5 | static const qint32 CONST_MIN_TIMEOUT{1000}; 6 | static const qreal PRECISSION{0.00000001}; 7 | static const qint32 CONST_ORDERS_COUNT{5}; 8 | static const qreal BINANCE_FEE{0.00075}; //NOTE: комиссия по BNB 9 | 10 | static const QString GLOBAL_APP_NAME{QStringLiteral("CryptoShloma")}; 11 | #define DOUBLE_TO_STR(val) QString::number(val, 'f', 8) 12 | -------------------------------------------------------------------------------- /resources/qml/ExchangePageSection.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | import QtQuick.Controls 2.12 3 | import BaseElements 1.0 4 | 5 | GroupBox { 6 | spacing: 0 7 | padding: 0 8 | label: EmptyLabel { 9 | x: parent.leftPadding 10 | width: parent.availableWidth 11 | text: parent.title 12 | horizontalAlignment: Text.AlignLeft 13 | elide: Text.ElideLeft 14 | } 15 | background: Rectangle { 16 | color: "transparent" 17 | border.width: 0 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /resources/ui_icons/cursor.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /resources/ui_icons/bookmark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /resources/ui_icons/cancel.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /resources/ui_icons/monitor.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/algo/hanukkah/zero_step.h: -------------------------------------------------------------------------------- 1 | #ifndef ZEROSTEP_H 2 | #define ZEROSTEP_H 3 | #include "i_step.h" 4 | 5 | class ZeroStep final : public IStep 6 | { 7 | public: 8 | ZeroStep(const qreal &declinePerc, 9 | const qreal &getGrowth, 10 | Hanukkah * const parentAlgo, 11 | const Market::ConstPtr &market, 12 | bool buyBlocked = false, 13 | bool sellBlocked = false) noexcept; 14 | void executeBuyLogic() noexcept override; 15 | void executeSellLogic() noexcept override; 16 | }; 17 | 18 | #endif // ZEROSTEP_H 19 | -------------------------------------------------------------------------------- /src/boilerplate/base_entry.h: -------------------------------------------------------------------------------- 1 | #ifndef BASE_ENTRY_H 2 | #define BASE_ENTRY_H 3 | #include "strong_typedef.h" 4 | 5 | namespace Boilerplate { 6 | 7 | template< 8 | typename TYPE, 9 | typename ID 10 | > 11 | class BaseEntry 12 | { 13 | public: 14 | virtual ~BaseEntry() = default; 15 | using ConstPtr = QSharedPointer; 16 | using Ptr = QSharedPointer; 17 | using Container = QList; 18 | using ConstContainer = QList; 19 | using Id = Boilerplate::StrongTypedef; 20 | }; 21 | } 22 | 23 | #endif // BASE_ENTRY_H 24 | -------------------------------------------------------------------------------- /resources/ui_icons/settings-2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /resources/default.cfg: -------------------------------------------------------------------------------- 1 | EnableTestData: false; 2 | TradeBaseSumm: 0.0006; 3 | MinSpread: 0.00000001; 4 | MinProfit: 1; 5 | MinBaseVolume: 5; 6 | MinCoinPrice: 0.000001; 7 | TradeDifference: 0.00000001; 8 | 9 | Hanukkah/HanukkahTable: [{\"step\":0,\"growth\":0},{\"step\":3,\"growth\":2},{\"step\":6,\"growth\":3},{\"step\":11,\"growth\":7},{\"step\":17,\"growth\":12},{\"step\":23,\"growth\":17},{\"step\":30,\"growth\":21},{\"step\":33,\"growth\":27}]; 10 | Hanukkah/HanukkahMaxSteps: 4; 11 | 12 | Binance/BinanceEnabled: true; 13 | Binance/BinanceAPIKey: xxxxxxx; 14 | Binance/BinanceSecretKey: xxxxxxx; 15 | -------------------------------------------------------------------------------- /resources/exchange_logos/binance.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /installer/installer.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = aux 2 | 3 | INSTALLER = CryptoShloma-installer-v0.3 4 | 5 | INPUT = $$PWD/config/config.xml $$PWD/packages 6 | installer.input = INPUT 7 | installer.output = $$INSTALLER 8 | installer.commands = ~/Qt/Tools/QtInstallerFramework/4.0/bin/binarycreator --offline-only -c $$PWD/config/config.xml -r $$PWD/resources/cryptoshloma.qrc -p $$PWD/packages ${QMAKE_FILE_OUT} 9 | installer.CONFIG += target_predeps no_link combine 10 | 11 | QMAKE_EXTRA_COMPILERS += installer 12 | 13 | OTHER_FILES += packages/cryptoshloma/meta/package.xml 14 | 15 | RESOURCES += resources/cryptoshloma.qrc 16 | -------------------------------------------------------------------------------- /resources/ui_icons/search.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/models/spread_markets_proxy_model.h: -------------------------------------------------------------------------------- 1 | #ifndef SPREADMARKETSPROXYMODEL_H 2 | #define SPREADMARKETSPROXYMODEL_H 3 | #include 4 | 5 | class SpreadMarketsProxyModel final : public QSortFilterProxyModel 6 | { 7 | Q_OBJECT 8 | public: 9 | explicit SpreadMarketsProxyModel(QObject *parent = nullptr) noexcept; 10 | protected: 11 | bool lessThan( const QModelIndex & left, const QModelIndex & right ) const noexcept override; 12 | bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const noexcept override; 13 | }; 14 | 15 | #endif // SPREADMARKETSPROXYMODEL_H 16 | -------------------------------------------------------------------------------- /src/models/volume_markets_proxy_model.h: -------------------------------------------------------------------------------- 1 | #ifndef VOLUMEMARKETSPROXYMODEL_H 2 | #define VOLUMEMARKETSPROXYMODEL_H 3 | #include 4 | 5 | class VolumeMarketsProxyModel final : public QSortFilterProxyModel 6 | { 7 | Q_OBJECT 8 | public: 9 | explicit VolumeMarketsProxyModel(QObject *parent = nullptr) noexcept; 10 | protected: 11 | bool lessThan( const QModelIndex & left, const QModelIndex & right ) const noexcept override; 12 | bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const noexcept override; 13 | }; 14 | 15 | #endif // VOLUMEMARKETSPROXYMODEL_H 16 | -------------------------------------------------------------------------------- /resources/ui_icons/layer.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /resources/ui_icons/refresh.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /resources/ui_icons/remove.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /resources/ui_icons/video-camera.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/algo/hanukkah/step.h: -------------------------------------------------------------------------------- 1 | #ifndef STEP_H 2 | #define STEP_H 3 | #include "i_step.h" 4 | 5 | class Step final : public IStep 6 | { 7 | public: 8 | Step(const qreal &declinePerc, 9 | const qreal &getGrowth, 10 | Hanukkah * const parentAlgo, 11 | const Market::ConstPtr &market, 12 | bool buyBlocked = false, 13 | bool sellBlocked = false) noexcept; 14 | void executeBuyLogic() noexcept override; 15 | void executeSellLogic() noexcept override; 16 | private: 17 | qreal calculatePrevVolume(const Order::ConstContainer &container) noexcept; 18 | }; 19 | 20 | #endif // STEP_H 21 | -------------------------------------------------------------------------------- /src/models/buy_orders_proxy_model.h: -------------------------------------------------------------------------------- 1 | #ifndef BUYORDERSPROXYMODEL_H 2 | #define BUYORDERSPROXYMODEL_H 3 | #include 4 | 5 | class BuyOrdersProxyModel final : public QSortFilterProxyModel 6 | { 7 | Q_OBJECT 8 | public: 9 | explicit BuyOrdersProxyModel(QObject *parent = nullptr) noexcept; 10 | // Q_INVOKABLE void refresh() noexcept; 11 | protected: 12 | bool lessThan(const QModelIndex &left, const QModelIndex &right) const noexcept override; 13 | bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const noexcept override; 14 | }; 15 | 16 | #endif // BUYORDERSPROXYMODEL_H 17 | -------------------------------------------------------------------------------- /resources/ui_icons/prohibition.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /resources/ui_icons/push-pin.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/models/sell_orders_proxy_model.h: -------------------------------------------------------------------------------- 1 | #ifndef SELLORDERSPROXYMODEL_H 2 | #define SELLORDERSPROXYMODEL_H 3 | #include 4 | 5 | class SellOrdersProxyModel final: public QSortFilterProxyModel 6 | { 7 | Q_OBJECT 8 | public: 9 | explicit SellOrdersProxyModel(QObject *parent = nullptr) noexcept; 10 | // Q_INVOKABLE void refresh() noexcept; 11 | protected: 12 | bool lessThan(const QModelIndex &left, const QModelIndex &right) const noexcept override; 13 | bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const noexcept override; 14 | }; 15 | 16 | #endif // SELLORDERSPROXYMODEL_H 17 | -------------------------------------------------------------------------------- /resources/qml/BaseElements/ListViewStyled.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | import QtQuick.Controls 2.12 3 | 4 | ListView { 5 | property bool busyEnabled: true 6 | BusyIndicatorStyled { 7 | id: indicator 8 | } 9 | 10 | onCountChanged: { 11 | indicator.running = (count > 0 ? false : true) && busyEnabled 12 | } 13 | 14 | clip: true 15 | contentWidth: headerItem.width 16 | flickableDirection: Flickable.VerticalFlick 17 | ScrollIndicator.vertical: ScrollIndicator { } 18 | // focus: true 19 | headerPositioning: ListView.OverlayHeader 20 | boundsBehavior: Flickable.StopAtBounds 21 | } 22 | -------------------------------------------------------------------------------- /resources/ui_icons/headphones.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /resources/ui_icons/unlock.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /resources/qml/BaseElements/qmldir: -------------------------------------------------------------------------------- 1 | module BaseElements 2 | BusyIndicatorStyled 1.0 BusyIndicatorStyled.qml 3 | DoubleSpinBox2Prec 1.0 DoubleSpinBox2Prec.qml 4 | DoubleSpinBox8Prec 1.0 DoubleSpinBox8Prec.qml 5 | EmptyLabel 1.0 EmptyLabel.qml 6 | HeaderLabel 1.0 HeaderLabel.qml 7 | ListViewDelegateStyled 1.0 ListViewDelegateStyled.qml 8 | ListViewHeaderStyled 1.0 ListViewHeaderStyled.qml 9 | ListViewStyled 1.0 ListViewStyled.qml 10 | PasswordField 1.0 PasswordField.qml 11 | Separator 1.0 Separator.qml 12 | TabBarIcon 1.0 TabBarIcon.qml 13 | TableToolButton 1.0 TableToolButton.qml 14 | ToolBarIcon 1.0 ToolBarIcon.qml 15 | SearchField 1.0 SearchField.qml 16 | -------------------------------------------------------------------------------- /resources/ui_icons/credit-card.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /resources/ui_icons/heart.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /launcher/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "ProcessObserver.h" 3 | #include 4 | #include 5 | #include 6 | 7 | int main(int argc, char *argv[]) 8 | { 9 | QCoreApplication app(argc, argv); 10 | 11 | app.setApplicationName("Launcher"); 12 | app.setOrganizationName(GLOBAL_APP_NAME); 13 | 14 | initLogger(&app, Logger::Debug); 15 | 16 | ProcessObserver observer(app.applicationDirPath() + "/" + GLOBAL_APP_NAME, app.arguments()); 17 | observer.start(); 18 | QObject::connect(&app, &QCoreApplication::aboutToQuit, &observer, &ProcessObserver::deleteLater); 19 | return app.exec(); 20 | } 21 | -------------------------------------------------------------------------------- /resources/ui_icons/moon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /resources/ui_icons/forward.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /resources/ui_icons/rewind.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /resources/ui_icons/clock.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /resources/ui_icons/menu.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /resources/ui_icons/profile.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /resources/ui_icons/magnet.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /resources/ui_icons/video.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /resources/ui_icons/lightning.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /resources/ui_icons/left-arrow-1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /resources/ui_icons/play-button.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /resources/ui_icons/shield.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /resources/ui_icons/add.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /resources/ui_icons/right-arrow-1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/algo/trend_searcher.h: -------------------------------------------------------------------------------- 1 | #ifndef TRENDSEARCHER_H 2 | #define TRENDSEARCHER_H 3 | #include "i_analytics_algo.h" 4 | 5 | class TrendSearcher final: public AbstractAnalyticsAlgorithm 6 | { 7 | Q_OBJECT 8 | public: 9 | explicit TrendSearcher(QObject *parent = nullptr) noexcept; 10 | private slots: 11 | void timerEvent(QTimerEvent *event) noexcept override final; 12 | bool start() noexcept override final; 13 | bool stop() noexcept override final; 14 | private: 15 | qreal calcPerc(const qreal &a, const qreal &b) const noexcept; 16 | qreal findFirstSellPrice(const Order::ConstContainer &model, const QDateTime &cycleTimeStart) noexcept; 17 | qint32 m_TimerId{0}; 18 | }; 19 | 20 | #endif // TRENDSEARCHER_H 21 | -------------------------------------------------------------------------------- /resources/ui_icons/bluetooth.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /resources/ui_icons/edit.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /resources/ui_icons/info.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /resources/ui_icons/cursor-1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /resources/ui_icons/power.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /resources/ui_icons/switch.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/models/balances_proxy_model.h: -------------------------------------------------------------------------------- 1 | #ifndef BALANCESPROXYMODEL_H 2 | #define BALANCESPROXYMODEL_H 3 | #include 4 | 5 | class BalancesProxyModel final: public QSortFilterProxyModel 6 | { 7 | Q_OBJECT 8 | public: 9 | explicit BalancesProxyModel(QObject *parent = nullptr) noexcept; 10 | // Q_INVOKABLE void refresh() noexcept; 11 | // void setFilterMarketId(const Market::MarketId &id) noexcept; 12 | protected: 13 | bool lessThan(const QModelIndex &left, const QModelIndex &right) const noexcept override; 14 | bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const noexcept override; 15 | //private: 16 | // Market::MarketId m_marketId; 17 | }; 18 | #endif // BALANCESPROXYMODEL_H 19 | -------------------------------------------------------------------------------- /resources/ui_icons/download.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /resources/ui_icons/zoom-out.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /include/Models: -------------------------------------------------------------------------------- 1 | #include "../src/models/markets_model.h" 2 | #include "../src/models/spread_markets_proxy_model.h" 3 | #include "../src/models/volume_markets_proxy_model.h" 4 | #include "../src/models/market_orders_model.h" 5 | #include "../src/models/market_orders_proxy_model.h" 6 | #include "../src/models/open_orders_model.h" 7 | #include "../src/models/open_orders_proxy_model.h" 8 | #include "../src/models/balances_model.h" 9 | #include "../src/models/balances_proxy_model.h" 10 | #include "../src/models/buy_orders_proxy_model.h" 11 | #include "../src/models/sell_orders_proxy_model.h" 12 | #include "../src/models/trading_pairs_proxy_model.h" 13 | #include "../src/models/trade_history_model.h" 14 | #include "../src/models/trade_history_proxy_model.h" 15 | -------------------------------------------------------------------------------- /resources/ui_icons/pause.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /resources/ui_icons/bag.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /resources/qml/BaseElements/DoubleSpinBox8Prec.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | import QtQuick.Controls 2.12 3 | 4 | SpinBox { 5 | id: spinbox 6 | from: 0 7 | value: 0 8 | to: 100000000 9 | stepSize: 1 10 | editable: true 11 | 12 | property int decimals: 8 13 | property real realValue: value / 100000000 14 | 15 | validator: DoubleValidator { 16 | bottom: Math.min(spinbox.from, spinbox.to) 17 | top: Math.max(spinbox.from, spinbox.to) 18 | } 19 | 20 | textFromValue: function(value, locale) { 21 | return Number(value / 100000000).toFixed(spinbox.decimals) 22 | } 23 | 24 | valueFromText: function(text, locale) { 25 | return Number.fromLocaleString(locale, text) * 100000000 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /resources/ui_icons/volume.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/models/market_orders_proxy_model.h: -------------------------------------------------------------------------------- 1 | #ifndef MARKERORDERSPROXYMODEL_H 2 | #define MARKERORDERSPROXYMODEL_H 3 | #include 4 | #include 5 | 6 | class MarketOrdersProxyModel; 7 | #pragma pack (push, 1) 8 | class MarketOrdersProxyModel final: public QSortFilterProxyModel 9 | { 10 | Q_OBJECT 11 | public: 12 | explicit MarketOrdersProxyModel(QObject *parent = nullptr) noexcept; 13 | public slots: 14 | void setFilterMarketId(const Market::MarketId &id = Market::MarketId()) noexcept; 15 | protected: 16 | bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const noexcept override; 17 | private: 18 | Market::MarketId m_marketId{}; 19 | }; 20 | #pragma pack (pop) 21 | #endif // MARKERORDERSPROXYMODEL_H 22 | -------------------------------------------------------------------------------- /resources/ui_icons/map.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /resources/qml/BaseElements/DoubleSpinBox2Prec.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | import QtQuick.Controls 2.12 3 | 4 | SpinBox { 5 | id: spinbox 6 | from: 0 7 | to: 100 * 100 8 | stepSize: 100 9 | // anchors.centerIn: parent 10 | 11 | property int decimals: 2 12 | property real realValue: value / 100 13 | 14 | validator: DoubleValidator { 15 | bottom: Math.min(spinbox.from, spinbox.to) 16 | top: Math.max(spinbox.from, spinbox.to) 17 | } 18 | 19 | textFromValue: function(value, locale) { 20 | return Number(value / 100).toLocaleString(locale, 'f', spinbox.decimals) 21 | } 22 | 23 | valueFromText: function(text, locale) { 24 | return Number.fromLocaleString(locale, text) * 100 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /resources/ui_icons/cloud.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /resources/ui_icons/cancel-1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /resources/ui_icons/hourglass.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /resources/ui_icons/visible.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/api/i_exchange_api_entry.h: -------------------------------------------------------------------------------- 1 | #ifndef I_EXCHANGE_API_ENTRY_H 2 | #define I_EXCHANGE_API_ENTRY_H 3 | #include 4 | #include 5 | 6 | namespace exchange { 7 | struct MarketEntry 8 | { 9 | Currency::CurrencyId tradeCurrencyId{}; 10 | Currency::CurrencyId baseCurrencyId{}; 11 | Market::Config params{}; 12 | }; 13 | struct OrderEntry 14 | { 15 | Market::MarketId marketId{}; 16 | Order::Config params{}; 17 | }; 18 | 19 | struct BalanceEntry 20 | { 21 | Currency::CurrencyId currencyId{}; 22 | Balance::Config params{}; 23 | }; 24 | 25 | using MarketEntryPtr = QSharedPointer; 26 | using OrderEntryPtr = QSharedPointer; 27 | using BalanceEntryPtr = QSharedPointer; 28 | } 29 | #endif // I_EXCHANGE_API_ENTRY_H 30 | -------------------------------------------------------------------------------- /resources/ui_icons/cut.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /resources/ui_icons/export.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /resources/ui_icons/paint.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /resources/ui_icons/battery.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /resources/ui_icons/favorite.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /resources/ui_icons/home.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /resources/ui_icons/cutlery.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /resources/ui_icons/speech-bubble.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /resources/ui_icons/briefcase.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/cryptoshloma.h: -------------------------------------------------------------------------------- 1 | #ifndef CRYPTOSHLOMA_H 2 | #define CRYPTOSHLOMA_H 3 | #include 4 | #include "models/exchange_pool.h" 5 | #include "include/version.hpp" 6 | 7 | class CryptoShloma final: public QObject 8 | { 9 | Q_OBJECT 10 | Q_PROPERTY(QString softwareVersion READ getSoftwareVersion CONSTANT) 11 | public: 12 | explicit CryptoShloma(QObject *parent = nullptr); 13 | ~CryptoShloma() noexcept; 14 | bool init(); 15 | const QString getSoftwareVersion() const { return cryptoShlomaVersionStr(); } 16 | public slots: 17 | void reloadSettings(const bool initial = false) noexcept; 18 | void restartEmulation() noexcept; 19 | private: 20 | void qmlRegistration() noexcept; 21 | 22 | QQmlApplicationEngine m_Engine{}; 23 | ExchangePool m_exchangePool{}; 24 | }; 25 | 26 | #endif // CRYPTOSHLOMA_H 27 | -------------------------------------------------------------------------------- /resources/ui_icons/correct.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /resources/ui_icons/garbage.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/models/trading_pairs_proxy_model.cpp: -------------------------------------------------------------------------------- 1 | #include "trading_pairs_proxy_model.h" 2 | #include "markets_model.h" 3 | 4 | TradingPairsProxyModel::TradingPairsProxyModel(QObject *parent) noexcept : 5 | QSortFilterProxyModel(parent) 6 | { 7 | setDynamicSortFilter(true); 8 | } 9 | 10 | bool TradingPairsProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const noexcept 11 | { 12 | const QModelIndex &indexRow = sourceModel()->index(sourceRow, 0, sourceParent); 13 | if ( !indexRow.isValid() ) { 14 | return false; 15 | } 16 | const auto &variant = sourceModel()->data( indexRow, MarketsModel::MarketRole ); 17 | Q_ASSERT_X(variant.canConvert< Market * >(), "QVariant convert", "TradingPairsProxyModel::filterAcceptsRow" ); 18 | const auto &market = variant.value< Market * >(); 19 | 20 | return market->getIsTradable(); 21 | } 22 | -------------------------------------------------------------------------------- /resources/ui_icons/printer.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /resources/ui_icons/zoom.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /resources/ui_icons/voice-recorder.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /resources/ui_icons/clip.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /resources/ui_icons/bell.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.github/workflows/install_qt.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: Install Qt 4 | 5 | # Controls when the action will run. 6 | on: 7 | # Allows you to run this workflow manually from the Actions tab 8 | workflow_dispatch: 9 | 10 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 11 | jobs: 12 | # This workflow contains a single job called "build" 13 | build: 14 | # The type of runner that the job will run on 15 | runs-on: ubuntu-latest 16 | 17 | # Steps represent a sequence of tasks that will be executed as part of the job 18 | steps: 19 | - name: Install Qt 20 | uses: jurplel/install-qt-action@v2 21 | with: 22 | version: '5.12.8' 23 | host: 'linux' 24 | target: 'desktop' 25 | install-deps: 'true' 26 | cached: 'false' 27 | setup-python: 'true' 28 | -------------------------------------------------------------------------------- /resources/ui_icons/screen.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /resources/ui_icons/tag.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /resources/ui_icons/share.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /resources/ui_icons/waiting.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/crud.h: -------------------------------------------------------------------------------- 1 | #ifndef CRUD_H 2 | #define CRUD_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | class Crud : public QObject 10 | { 11 | Q_OBJECT 12 | Q_PROPERTY(bool isOpen READ getIsOpen NOTIFY isOpenChanged) 13 | 14 | Crud (const Crud &) noexcept; 15 | Crud &operator = (const Crud &) noexcept; 16 | explicit Crud(QObject *parent = nullptr) noexcept; 17 | ~Crud() noexcept; 18 | static Crud *m_Crud; 19 | public: 20 | static Crud *getInstance(); 21 | static void drop(); 22 | void openDatabase(); 23 | void closeDatabase(); 24 | bool getIsOpen() const; 25 | QSqlQuery executeQuery(const QString &sql); 26 | signals: 27 | void isOpenChanged(); 28 | private: 29 | QSqlDatabase m_Database{}; 30 | mutable QReadWriteLock mutex{}; 31 | bool m_isOpen{false}; 32 | }; 33 | 34 | #endif // CRUD_H 35 | -------------------------------------------------------------------------------- /resources/ui_icons/target.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # C++ objects and libs 2 | *.slo 3 | *.lo 4 | *.o 5 | *.a 6 | *.la 7 | *.lai 8 | *.so 9 | *.so.* 10 | *.dll 11 | *.dylib 12 | 13 | # Qt-es 14 | object_script.*.Release 15 | object_script.*.Debug 16 | *_plugin_import.cpp 17 | /.qmake.cache 18 | /.qmake.stash 19 | *.pro.user 20 | *.pro.user.* 21 | *.qbs.user 22 | *.qbs.user.* 23 | *.moc 24 | moc_*.cpp 25 | moc_*.h 26 | qrc_*.cpp 27 | ui_*.h 28 | *.qmlc 29 | *.jsc 30 | Makefile* 31 | *build-* 32 | *.qm 33 | *.prl 34 | 35 | # Qt unit tests 36 | target_wrapper.* 37 | 38 | # QtCreator 39 | *.autosave 40 | 41 | # QtCreator Qml 42 | *.qmlproject.user 43 | *.qmlproject.user.* 44 | 45 | # QtCreator CMake 46 | CMakeLists.txt.user* 47 | 48 | # QtCreator 4.8< compilation database 49 | compile_commands.json 50 | 51 | # QtCreator local machine specific files for imported projects 52 | *creator.user* 53 | 54 | #Project files 55 | include/version.hpp 56 | installer/packages/cryptoshloma/data/CryptoShloma.7z 57 | -------------------------------------------------------------------------------- /resources/ui_icons/shutter.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /resources/ui_icons/file.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /resources/ui_icons/gamepad.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /resources/ui_icons/placeholder.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /resources/ui_icons/timer.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /resources/ui_icons/calendar.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /resources/ui_icons/padnote.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /resources/ui_icons/layout.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/models/open_orders_proxy_model.h: -------------------------------------------------------------------------------- 1 | #ifndef OPENORDERSPROXYMODEL_H 2 | #define OPENORDERSPROXYMODEL_H 3 | #include 4 | #include 5 | 6 | class OpenOrdersProxyModel; 7 | #pragma pack (push, 1) 8 | class OpenOrdersProxyModel final: public QSortFilterProxyModel 9 | { 10 | Q_OBJECT 11 | public: 12 | explicit OpenOrdersProxyModel(QObject *parent = nullptr) noexcept; 13 | public slots: 14 | void setFilterOrderType(const Order::OrderType type) noexcept; 15 | void setFilterMarketId(const Market::MarketId &id) noexcept; 16 | // void refresh() noexcept; 17 | protected: 18 | bool lessThan(const QModelIndex &left, const QModelIndex &right) const noexcept override; 19 | bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const noexcept override; 20 | private: 21 | Order::OrderType m_OrderTypeFilter{Order::None}; 22 | Market::MarketId m_marketId{}; 23 | }; 24 | #pragma pack (pop) 25 | 26 | #endif // OPENORDERSPROXYMODEL_H 27 | -------------------------------------------------------------------------------- /resources/ui_icons/logout.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /resources/qml/Emulation.qml: -------------------------------------------------------------------------------- 1 | import QtQuick.Dialogs 1.3 2 | import QtQuick 2.12 3 | import QtQuick.Controls 2.12 4 | import QtQuick.Controls.Material 2.12 5 | 6 | Popup { 7 | property alias dialog: fileDialog 8 | width: 250 9 | height: 70 10 | Material.theme: Material.Dark 11 | modal: false 12 | 13 | FileDialog { 14 | id: fileDialog 15 | title: qsTr("Please choose a file") 16 | folder: shortcuts.home 17 | nameFilters: [qsTr("Comma-Separated Values (*.csv)")] 18 | onAccepted: { 19 | var exchange = pageRepeater.itemAt(mainWrapper.currentIndex) 20 | if (exchange.item !== undefined && exchange.status === Loader.Ready) 21 | exchange.item.api.emulator.loadMarketOrders(fileDialog.fileUrl) 22 | } 23 | } 24 | 25 | Column { 26 | anchors.fill: parent 27 | ToolButton { 28 | text: qsTr("Load market orders") 29 | onClicked: fileDialog.open() 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/storage/balances_storage.h: -------------------------------------------------------------------------------- 1 | #ifndef BALANCES_STORAGE_H 2 | #define BALANCES_STORAGE_H 3 | 4 | #include 5 | #include 6 | class IExchangeAPI; 7 | 8 | class BalancesStorage final : public QObject 9 | { 10 | Q_OBJECT 11 | public: 12 | explicit BalancesStorage(IExchangeAPI *parent = nullptr) noexcept; 13 | explicit BalancesStorage(const BalancesStorage &o) noexcept = delete; 14 | BalancesStorage& operator=(const BalancesStorage &o) noexcept = delete ; 15 | public: 16 | Balance::ConstPtr find(const Currency::ConstPtr ¤cy) const noexcept; 17 | Balance::ConstPtr find(const Currency::CurrencyId &id) const noexcept; 18 | Balance::ConstPtr btcBalance() const noexcept; //TODO: deprecated 19 | public slots: 20 | void merge(const QList &data) noexcept; 21 | private: 22 | QList m_Data{}; 23 | IExchangeAPI * const m_api; 24 | QReadWriteLock lock{}; 25 | }; 26 | 27 | #endif // BALANCES_STORAGE_H 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /resources/ui_icons/photo-camera.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /cmake/utils.cmake: -------------------------------------------------------------------------------- 1 | # Влючение всех предупреждений 2 | macro(enable_all_warnings) 3 | if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic -Weffc++ -Woverloaded-virtual -Wctor-dtor-privacy -Wnon-virtual-dtor -Wold-style-cast -Wconversion -Wsign-conversion -Winit-self -Wunreachable-code") 5 | elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") 6 | if (CMAKE_CXX_FLAGS MATCHES "/W[0-4]") 7 | string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") 8 | else () 9 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4") 10 | endif () 11 | elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") 12 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic") #TODO 13 | else () 14 | message(SEND_ERROR "Включение всех предупреждений для используемой версии компилятора не реализовано") 15 | endif () 16 | add_definitions(-DQT_DEPRECATED_WARNINGS) 17 | endmacro(enable_all_warnings) 18 | -------------------------------------------------------------------------------- /cmake/version.in: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | /*! Вовзарщает Major версию*/ 6 | #define CryptoShloma_MAJOR_VERSION @CryptoShloma_VERSION_MAJOR@ 7 | 8 | /*! Возвращает Minor версию */ 9 | #define CryptoShloma_MINOR_VERSION @CryptoShloma_VERSION_MINOR@ 10 | 11 | #define CryptoShloma_PATCH_VERSION @CryptoShloma_VERSION_PATCH@ 12 | 13 | #define CryptoShloma_REVISION @CryptoShloma_REVISION@ 14 | 15 | #define CryptoShloma_BRANCH "@CryptoShloma_BRANCH@" 16 | 17 | #define CryptoShloma_FULL_VERSION "@CryptoShloma_VERSION@" 18 | 19 | #define CryptoShloma_DB_VERSION "@CryptoShloma_DB@" 20 | 21 | /*! \brief Возвращает текущую версию в виде строки 22 | */ 23 | inline QString cryptoShlomaVersionStr() noexcept 24 | { 25 | return QString( CryptoShloma_FULL_VERSION ); 26 | } 27 | 28 | /*! \brief Возвращает текущую версию бд виде строки 29 | */ 30 | inline QString cryptoShlomaDatabaseVersionStr() noexcept 31 | { 32 | return QString( CryptoShloma_DB_VERSION ); 33 | } 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /installer/packages/cryptoshloma/meta/installscript.qs: -------------------------------------------------------------------------------- 1 | function Component() 2 | { 3 | // default constructor 4 | } 5 | 6 | Component.prototype.createOperations = function() 7 | { 8 | // call default implementation to actually install README.txt! 9 | component.createOperations(); 10 | 11 | if (systemInfo.productType === "windows") { 12 | component.addOperation("CreateShortcut", "@TargetDir@/Launcher.exe", "@StartMenuDir@/CryptoShloma.lnk", 13 | "workingDirectory=@TargetDir@", "iconPath=@TargetDir@/Launcher.exe", 14 | "iconId=0", "description=CryptoShloma"); 15 | component.addOperation("CreateShortcut", 16 | "@TargetDir@/Launcher.exe",// target 17 | "@DesktopDir@/CryptoShloma.lnk",// link-path 18 | "workingDirectory=@TargetDir@",// working-dir 19 | "iconPath=@TargetDir@/Launcher.exe", "iconId=0",// icon 20 | "description=CryptoShloma");// description 21 | } 22 | } 23 | 24 | -------------------------------------------------------------------------------- /resources/ui_icons/gallery.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /resources/ui_icons/presentation.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/storage/trade_history_storage.h: -------------------------------------------------------------------------------- 1 | #ifndef TRADE_HISTORY_STORAGE_H 2 | #define TRADE_HISTORY_STORAGE_H 3 | 4 | #include 5 | #include 6 | class IExchangeAPI; 7 | 8 | class TradeHistoryStorage final: public QObject 9 | { 10 | Q_OBJECT 11 | public: 12 | explicit TradeHistoryStorage(IExchangeAPI *parent = nullptr) noexcept; 13 | explicit TradeHistoryStorage(const TradeHistoryStorage &o) noexcept = delete; 14 | TradeHistoryStorage& operator=(const TradeHistoryStorage &o) noexcept = delete; 15 | Order::ConstContainer filter(const Market::MarketId &marketId) const noexcept; 16 | public slots: 17 | void insert(const Market::MarketId &id) noexcept; 18 | void merge(const QList &data) noexcept; 19 | void remove(const Market::MarketId &id) noexcept; 20 | private: 21 | Order::Container m_Data{}; 22 | QList m_Filters{}; 23 | IExchangeAPI * const m_api; 24 | QReadWriteLock lock{}; 25 | }; 26 | 27 | #endif // TRADE_HISTORY_STORAGE_H 28 | -------------------------------------------------------------------------------- /launcher/ProcessObserver.h: -------------------------------------------------------------------------------- 1 | #ifndef PROCESSOBSERVER_H 2 | #define PROCESSOBSERVER_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | class ProcessObserver : public QObject 9 | { 10 | Q_OBJECT 11 | public: 12 | explicit ProcessObserver(const QString& path, const QStringList& arguments); 13 | ~ProcessObserver(); 14 | 15 | public slots: 16 | void start(); 17 | private: 18 | void saveAndExit(); 19 | private slots: 20 | void onFinished(int exitCode, QProcess::ExitStatus exitStatus); 21 | void onStarted(); 22 | void onCheckProcess(); 23 | private: 24 | QProcess m_process{}; /// < Процесс, который будем запускать 25 | QString m_processPath; 26 | QStringList m_arguments; 27 | QTime m_elapsedTime{}; /// < Время работы процесса 28 | bool m_wasStarted; /// < Флаг того, что процесс был запущен 29 | QTimer m_checkTimer{}; /// < Таймер проверки наличия процесса 30 | }; 31 | 32 | #endif // PROCESSOBSERVER_H 33 | -------------------------------------------------------------------------------- /resources/ui_icons/shopping-cart.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /resources/ui_icons/upload.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/storage/market_orders_storage.h: -------------------------------------------------------------------------------- 1 | #ifndef MARKET_ORDERS_STORAGE_H 2 | #define MARKET_ORDERS_STORAGE_H 3 | 4 | #include 5 | #include 6 | class IExchangeAPI; 7 | 8 | class MarketOrdersStorage final : public QObject 9 | { 10 | Q_OBJECT 11 | public: 12 | explicit MarketOrdersStorage(IExchangeAPI *parent) noexcept; 13 | explicit MarketOrdersStorage(const MarketOrdersStorage &o) noexcept = delete; 14 | MarketOrdersStorage& operator=(const MarketOrdersStorage &o) noexcept = delete; 15 | Order::ConstContainer filter(const Market::MarketId &marketId, const Order::OrderType orderType) const noexcept; 16 | public slots: 17 | void insert(const Market::MarketId &id) noexcept; 18 | void merge(const QList &data) noexcept; 19 | void remove(const Market::MarketId &id) noexcept; 20 | private: 21 | Order::Container m_Data{}; 22 | QList m_Filters{}; 23 | IExchangeAPI * const m_api; 24 | QReadWriteLock lock{}; 25 | }; 26 | 27 | #endif // MARKET_ORDERS_STORAGE_H 28 | -------------------------------------------------------------------------------- /resources/ui_icons/stats.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/storage/open_orders_storage.h: -------------------------------------------------------------------------------- 1 | #ifndef OPEN_ORDERS_STORAGE_H 2 | #define OPEN_ORDERS_STORAGE_H 3 | 4 | #include 5 | #include 6 | class IExchangeAPI; 7 | 8 | class OpenOrdersStorage final : public QObject 9 | { 10 | Q_OBJECT 11 | public: 12 | explicit OpenOrdersStorage(IExchangeAPI *parent) noexcept; 13 | explicit OpenOrdersStorage(const OpenOrdersStorage &o) noexcept = delete; 14 | OpenOrdersStorage& operator=(const OpenOrdersStorage &o) noexcept = delete; 15 | Order::ConstContainer filter(const Market::MarketId &marketId, const Order::OrderType orderType) const noexcept; 16 | qint32 count() const noexcept; 17 | public: 18 | Order::ConstContainer ordersByMarketId(const Market::MarketId &id) const noexcept; 19 | public slots: 20 | void merge(const QList &data) noexcept; 21 | void remove(const QList &data) noexcept; 22 | private: 23 | Order::Container m_Data{}; 24 | IExchangeAPI * const m_api; 25 | QReadWriteLock lock{}; 26 | }; 27 | 28 | #endif // OPEN_ORDERS_STORAGE_H 29 | -------------------------------------------------------------------------------- /resources/ui_icons/settings.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/models/market_orders_proxy_model.cpp: -------------------------------------------------------------------------------- 1 | #include "market_orders_proxy_model.h" 2 | #include "market_orders_model.h" 3 | 4 | MarketOrdersProxyModel::MarketOrdersProxyModel(QObject *parent) noexcept : 5 | QSortFilterProxyModel(parent) 6 | { 7 | setDynamicSortFilter(true); 8 | } 9 | 10 | void MarketOrdersProxyModel::setFilterMarketId(const Market::MarketId &id) noexcept 11 | { 12 | m_marketId = id; 13 | invalidateFilter(); 14 | } 15 | 16 | bool MarketOrdersProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const noexcept 17 | { 18 | const QModelIndex &rowIndex = sourceModel()->index(sourceRow, 0, sourceParent); 19 | if ( !rowIndex.isValid() ) { 20 | return false; 21 | } 22 | const auto &variant = sourceModel()->data( rowIndex, MarketOrdersModel::OrderRole ); 23 | Q_ASSERT_X(variant.canConvert< Order * >(), "QVariant convert", "MarkerOrdersProxyModel::filterAcceptsRow" ); 24 | 25 | const auto &order = variant.value< Order * >(); 26 | 27 | if ( m_marketId.isValid() ) 28 | return order->getMarket()->getId() == m_marketId; 29 | else return false; 30 | } 31 | -------------------------------------------------------------------------------- /resources/ui_icons/link.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /resources/ui_icons/envelope.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /resources/ui_icons/wifi.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/models/balances_model.h: -------------------------------------------------------------------------------- 1 | #ifndef BALANCESMODEL_H 2 | #define BALANCESMODEL_H 3 | #include 4 | #include 5 | 6 | class BalancesModel; 7 | class ExchangeEntry; 8 | //TODO: сделать final, убрать include balance 9 | class BalancesModel : public QAbstractListModel 10 | { 11 | Q_OBJECT 12 | public: 13 | enum BalancesModelRoles { 14 | BalanceRole = Qt::UserRole + 1 15 | }; 16 | 17 | explicit BalancesModel(ExchangeEntry *parent = nullptr) noexcept; 18 | explicit BalancesModel(const BalancesModel &o) noexcept = delete; 19 | BalancesModel& operator=(const BalancesModel &o) noexcept = delete; 20 | private: 21 | int rowCount(const QModelIndex &parent = QModelIndex()) const noexcept override; 22 | QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const noexcept override; 23 | QHash roleNames() const noexcept override; 24 | public slots: 25 | void merge(const QList &data) noexcept; 26 | void refresh() noexcept; 27 | private: 28 | QList m_Data{}; 29 | ExchangeEntry const * m_entry; 30 | }; 31 | #endif // BALANCESMODEL_H 32 | -------------------------------------------------------------------------------- /resources/ui_icons/speedometer.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/models/open_orders_model.h: -------------------------------------------------------------------------------- 1 | #ifndef OPENORDERSMODEL_H 2 | #define OPENORDERSMODEL_H 3 | #include 4 | #include 5 | 6 | class OpenOrdersModel; 7 | class ExchangeEntry; 8 | //TODO: сделать final 9 | class OpenOrdersModel : public QAbstractListModel 10 | { 11 | Q_OBJECT 12 | public: 13 | enum OpenOrdersModelRoles { 14 | OrderRole = Qt::UserRole + 1 15 | }; 16 | 17 | explicit OpenOrdersModel(ExchangeEntry *parent = nullptr) noexcept; 18 | explicit OpenOrdersModel(const OpenOrdersModel &o) noexcept = delete; 19 | OpenOrdersModel& operator=(const OpenOrdersModel &o) noexcept = delete; 20 | private: 21 | int rowCount(const QModelIndex &parent = QModelIndex()) const noexcept override; 22 | QVariant data(const QModelIndex &index, int role = Qt::UserRole) const noexcept override; 23 | QHash roleNames() const noexcept override; 24 | 25 | public slots: 26 | void merge(const QList &data) noexcept; 27 | void remove(const QList &data) noexcept; 28 | void refresh() noexcept; 29 | private: 30 | Order::Container m_Data{}; 31 | ExchangeEntry const * m_entry; 32 | }; 33 | #endif // OPENORDERSMODEL_H 34 | -------------------------------------------------------------------------------- /resources/ui_icons/puzzle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/api/binance_api_controller.h: -------------------------------------------------------------------------------- 1 | #ifndef BINANCE_API_CONTROLLER_H 2 | #define BINANCE_API_CONTROLLER_H 3 | #include "i_exchange_api_controller.h" 4 | 5 | class BinanceAPIController final : public IExchangeApiController 6 | { 7 | Q_OBJECT 8 | public: 9 | explicit BinanceAPIController(IExchangeAPI *api) noexcept; 10 | ~BinanceAPIController() noexcept override; 11 | private slots: 12 | void run() noexcept final; 13 | void timerEvent(QTimerEvent *event) noexcept override final; 14 | void appendMarket(const Market::MarketId &marketId, 15 | const Market::UserConfig &config) override final; 16 | void appendMarketsGroup(const MarketsGroup &group) override final; 17 | void removeMarket(const Market::MarketId &marketId) override final; 18 | void cancelAllOrders() override final; 19 | void cancelOrder(const Market::MarketId &marketId, 20 | const Order::OrderId &orderId) override final; 21 | void submitOrder(const Market::MarketId &marketId, 22 | const qreal &price, 23 | const qreal ¤cyVolume) override final; 24 | void dropCoin(const Currency::CurrencyId ¤cyId) override final; 25 | private: 26 | qint32 m_marketInfoTimerId = 0; 27 | }; 28 | 29 | #endif // BINANCE_API_CONTROLLER_H 30 | -------------------------------------------------------------------------------- /src/models/trade_history_proxy_model.h: -------------------------------------------------------------------------------- 1 | #ifndef TRADEHISTORYPROXYMODEL_H 2 | #define TRADEHISTORYPROXYMODEL_H 3 | #include 4 | #include 5 | 6 | class TradeHistoryProxyModel; 7 | #pragma pack (push, 1) 8 | class TradeHistoryProxyModel : public QSortFilterProxyModel 9 | { 10 | Q_OBJECT 11 | public: 12 | explicit TradeHistoryProxyModel(QObject *parent = nullptr) noexcept; 13 | public slots: 14 | void setFilterOrderType(const Order::OrderType type) noexcept; 15 | void setFilterMarketId(const Market::MarketId &id = Market::MarketId()) noexcept; 16 | protected: 17 | bool lessThan(const QModelIndex &left, const QModelIndex &right) const noexcept override; 18 | bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const noexcept override; 19 | protected: 20 | Market::MarketId m_marketId{}; 21 | private: 22 | Order::OrderType m_OrderTypeFilter{Order::None}; 23 | }; 24 | 25 | class TradeHistorySelectedPairsProxyModel final: public TradeHistoryProxyModel 26 | { 27 | Q_OBJECT 28 | public: 29 | explicit TradeHistorySelectedPairsProxyModel(QObject *parent = nullptr) noexcept; 30 | protected: 31 | bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const noexcept override; 32 | }; 33 | #pragma pack (pop) 34 | #endif // TRADEHISTORYPROXYMODEL_H 35 | -------------------------------------------------------------------------------- /src/models/market_orders_model.h: -------------------------------------------------------------------------------- 1 | #ifndef MARKETORDERSMODEL_H 2 | #define MARKETORDERSMODEL_H 3 | #include 4 | #include 5 | 6 | class MarketOrdersModel; 7 | class ExchangeEntry; 8 | //TODO: сделать final 9 | class MarketOrdersModel : public QAbstractListModel 10 | { 11 | Q_OBJECT 12 | public: 13 | enum MarketOrdersModelRoles { 14 | OrderRole = Qt::UserRole + 1 15 | }; 16 | 17 | explicit MarketOrdersModel(ExchangeEntry *parent = nullptr) noexcept; 18 | explicit MarketOrdersModel(const MarketOrdersModel &o) noexcept = delete; 19 | MarketOrdersModel& operator=(const MarketOrdersModel &o) noexcept = delete; 20 | private: 21 | int rowCount(const QModelIndex &parent = QModelIndex()) const noexcept override; 22 | QVariant data(const QModelIndex &index, int role = Qt::UserRole) const noexcept override; 23 | QHash roleNames() const noexcept override; 24 | 25 | public slots: 26 | void insert(const Market::MarketId &id) noexcept; 27 | void merge(const QList &data) noexcept; 28 | void remove(const Market::MarketId &id) noexcept; 29 | void refresh() noexcept; 30 | private: 31 | Order::Container m_Data{}; 32 | QList m_Filters{}; 33 | ExchangeEntry const *m_entry; 34 | }; 35 | #endif // MARKETORDERSMODEL_H 36 | -------------------------------------------------------------------------------- /share/sql/dbCryptoShloma.sqlite.sql: -------------------------------------------------------------------------------- 1 | BEGIN TRANSACTION; 2 | CREATE TABLE IF NOT EXISTS `markets_cache` ( 3 | `market_id` TEXT NOT NULL UNIQUE, 4 | `min_trend` REAL NOT NULL, 5 | `cur_trend_rate` REAL NOT NULL, 6 | `sell_mode` INTEGER, 7 | `pause_mode` INTEGER, 8 | `percentage_profit` REAL, 9 | `trade_summ` REAL, 10 | `is_volume_trading` INTEGER, 11 | `meta_config` TEXT, 12 | `exchange` INTEGER, 13 | `timestamp` TEXT NOT NULL, 14 | PRIMARY KEY(`market_id`, `exchange`) 15 | ); 16 | 17 | CREATE TABLE IF NOT EXISTS `trade_journal` ( 18 | `id` INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, 19 | `market_id` TEXT NOT NULL, 20 | `type` TEXT NOT NULL, 21 | `top_rate` REAL, 22 | `rate` REAL, 23 | `stop_loss` REAL, 24 | `balance` REAL, 25 | `amount` REAL, 26 | `total` REAL, 27 | `exchange` INTEGER, 28 | `timestamp` TEXT NOT NULL 29 | ); 30 | CREATE TABLE IF NOT EXISTS `errors` ( 31 | `id` INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, 32 | `type` TEXT NOT NULL, 33 | `text` TEXT NOT NULL, 34 | `request` BLOB, 35 | `result` BLOB, 36 | `exchange` INTEGER, 37 | `timestamp` TEXT NOT NULL 38 | ); 39 | CREATE TABLE IF NOT EXISTS `variables` ( 40 | `key` TEXT PRIMARY KEY, 41 | `value` TEXT 42 | ); 43 | COMMIT; 44 | -------------------------------------------------------------------------------- /installer/config/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | CryptoShloma 4 | 0.3 5 | Installer of CryptoShloma 6 | Man-k28 7 | https://github.com/man-k28/CryptoShloma 8 | CryptoShloma 9 | @ApplicationsDir@/CryptoShloma 10 | rabbi.png 11 | rabbi.png 12 | rabbi.png 13 | Updater 14 | 15 | 16 | 17 | 36 | -------------------------------------------------------------------------------- /src/models/trade_history_model.h: -------------------------------------------------------------------------------- 1 | #ifndef TRADEHISTORYMODEL_H 2 | #define TRADEHISTORYMODEL_H 3 | #include 4 | #include 5 | 6 | class TradeHistoryModel; 7 | class ExchangeEntry; 8 | //TODO: сделать final 9 | class TradeHistoryModel : public QAbstractListModel 10 | { 11 | Q_OBJECT 12 | public: 13 | enum TradeHistoryModelRoles { 14 | OrderRole = Qt::UserRole + 1 15 | }; 16 | 17 | explicit TradeHistoryModel(ExchangeEntry *parent = nullptr) noexcept; 18 | explicit TradeHistoryModel(const TradeHistoryModel &o) noexcept = delete; 19 | TradeHistoryModel& operator=(const TradeHistoryModel &o) noexcept = delete; 20 | const Order::ConstPtr findByIndex(const qint32 index) const noexcept; 21 | private: 22 | int rowCount(const QModelIndex &parent = QModelIndex()) const noexcept override; 23 | QVariant data(const QModelIndex &index, int role = Qt::UserRole) const noexcept override; 24 | QHash roleNames() const noexcept override; 25 | public slots: 26 | void insert(const Market::MarketId &id) noexcept; 27 | void merge(const QList &data) noexcept; 28 | void remove(const Market::MarketId &id) noexcept; 29 | void refresh() noexcept; 30 | private: 31 | Order::Container m_Data{}; 32 | QList m_Filters{}; 33 | ExchangeEntry const * m_entry; 34 | }; 35 | #endif // TRADEHISTORYMODEL_H 36 | -------------------------------------------------------------------------------- /resources/ui_icons/rocket-launch.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/algo/i_trade_algo.h: -------------------------------------------------------------------------------- 1 | #ifndef ITRADEALGORITHM_H 2 | #define ITRADEALGORITHM_H 3 | #include 4 | #include 5 | #include 6 | 7 | class IExchangeAPI; 8 | 9 | #pragma pack (push, 1) 10 | class ITradeAlgorithm : public QObject, public QRunnable 11 | { 12 | Q_OBJECT 13 | public: 14 | using ConstPtr = QSharedPointer; 15 | using Ptr = QSharedPointer; 16 | public: 17 | explicit ITradeAlgorithm(QObject *parent = nullptr) noexcept; 18 | virtual ~ITradeAlgorithm() noexcept override = default; 19 | 20 | static Ptr createAlgo(const AlgoType type, const Exchange::Type exchType) noexcept; 21 | void setApi(const QSharedPointer &api) noexcept; 22 | 23 | inline const QSharedPointer &api() const noexcept 24 | { 25 | return m_Api; 26 | } 27 | public: 28 | void run() noexcept override final; 29 | public slots: 30 | void init() noexcept; 31 | virtual bool start() = 0; 32 | //TODO: пока не обрабатывается 33 | virtual bool stop() = 0; 34 | virtual bool restart() = 0; 35 | virtual void reconfigure() = 0; 36 | virtual void removeMarket(const Market::MarketId &) {} 37 | signals: 38 | void updateMarketConfig(const Market::MarketId &marketId, const Market::UserConfig &config); 39 | void ready(); 40 | void finished(); 41 | private: 42 | QSharedPointer m_Api = {}; 43 | }; 44 | #pragma pack (pop) 45 | #endif // ITRADEALGORITHM_H 46 | -------------------------------------------------------------------------------- /src/algo/i_analytics_algo.h: -------------------------------------------------------------------------------- 1 | #ifndef ABSTRACTANALYTICSALGORITHM_H 2 | #define ABSTRACTANALYTICSALGORITHM_H 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | class ITradeAlgorithm; 9 | 10 | #pragma pack (push, 1) 11 | class AbstractAnalyticsAlgorithm : public QObject, public QRunnable 12 | { 13 | Q_OBJECT 14 | public: 15 | explicit AbstractAnalyticsAlgorithm(QObject *parent = nullptr) noexcept; 16 | virtual ~AbstractAnalyticsAlgorithm() noexcept override = default; 17 | using Ptr = QSharedPointer; 18 | static Ptr createAlgo(const AnalyticsAlgoType type) noexcept; 19 | 20 | void setTradeAlgo(const QSharedPointer &algo) noexcept; 21 | 22 | QSharedPointer tradeAlgo() const; 23 | public: 24 | void run() noexcept override final; 25 | public slots: 26 | virtual bool start() = 0; 27 | //TODO: пока не обрабатывается 28 | virtual bool stop() = 0; 29 | signals: 30 | void ready(); 31 | void finished(); 32 | void updateMarketConfig(const Market::MarketId &marketId, const Market::UserConfig &config); 33 | void cancelOrderId(const Market::MarketId &marketId, const Order::OrderId &id); 34 | QT_DEPRECATED void submitSellOrderToBuyPrice(const Market::MarketId &id, const qreal &price, const qreal ¤cyVolume); 35 | protected: 36 | QSharedPointer m_TradeAlgo = {}; 37 | }; 38 | #pragma pack (pop) 39 | #endif // ABSTRACTANALYTICSALGORITHM_H 40 | -------------------------------------------------------------------------------- /src/emulator.h: -------------------------------------------------------------------------------- 1 | #ifndef EMULATOR_H 2 | #define EMULATOR_H 3 | 4 | #include 5 | 6 | class IExchangeAPI; 7 | 8 | class Emulator : public QObject 9 | { 10 | Q_OBJECT 11 | public: 12 | explicit Emulator(IExchangeAPI *parent) noexcept; 13 | 14 | public slots: 15 | void loadMarketOrders(const QUrl &url) noexcept; 16 | 17 | void submitOrder(const Market::MarketId &marketId, 18 | const Order::OrderId &orderId, 19 | const Order::OrderType side, 20 | const qreal &price, 21 | const qreal &baseVolume, 22 | const qreal ¤cyVolume = 0.) noexcept; 23 | void cancelOrder(const Order::OrderId &orderId = Order::OrderId(0), 24 | const Market::MarketId &marketId = Market::MarketId()) noexcept; 25 | void sellOrder(const Market::MarketId &marketId, 26 | const Order::OrderId &orderId, 27 | const qreal &price, 28 | const qreal &baseVolume, 29 | const qreal ¤cyVolume) noexcept; 30 | signals: 31 | void loadedMarketOrders(const QList &list); 32 | void loadedUserOpenOrders(const QList &list); 33 | void loadedBalance(const QList &list); 34 | void loadedUserTradeHistory(const QList &list); 35 | void loadedCanceledOrders(const QList &ids); 36 | }; 37 | 38 | #endif // EMULATOR_H 39 | -------------------------------------------------------------------------------- /cmake/VersionResource.rc: -------------------------------------------------------------------------------- 1 | #include "VersionInfo.h" 2 | 3 | #if defined(__MINGW64__) || defined(__MINGW32__) 4 | // MinGW-w64, MinGW 5 | #if defined(__has_include) && __has_include() 6 | #include 7 | #else 8 | #include 9 | #include 10 | #endif 11 | #else 12 | // MSVC, Windows SDK 13 | #include 14 | #endif 15 | 16 | IDI_ICON1 ICON PRODUCT_ICON 17 | 18 | LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT 19 | 20 | VS_VERSION_INFO VERSIONINFO 21 | FILEVERSION FILE_VERSION_RESOURCE 22 | PRODUCTVERSION PRODUCT_VERSION_RESOURCE 23 | FILEFLAGSMASK 0x3fL 24 | #ifdef _DEBUG 25 | FILEFLAGS 0x1L 26 | #else 27 | FILEFLAGS 0x0L 28 | #endif 29 | FILEOS 0x4L 30 | FILETYPE 0x1L 31 | FILESUBTYPE 0x0L 32 | BEGIN 33 | BLOCK "StringFileInfo" 34 | BEGIN 35 | BLOCK "000904b0" 36 | BEGIN 37 | VALUE "Comments", PRODUCT_COMMENTS 38 | VALUE "CompanyName", PRODUCT_COMPANY_NAME 39 | VALUE "FileDescription", PRODUCT_FILE_DESCRIPTION 40 | VALUE "FileVersion", FILE_VERSION_RESOURCE_STR 41 | VALUE "InternalName", PRODUCT_INTERNAL_NAME 42 | VALUE "LegalCopyright", PRODUCT_COMPANY_COPYRIGHT 43 | VALUE "OriginalFilename", PRODUCT_ORIGINAL_FILENAME 44 | VALUE "ProductName", PRODUCT_BUNDLE 45 | VALUE "ProductVersion", PRODUCT_VERSION_RESOURCE_STR 46 | END 47 | END 48 | BLOCK "VarFileInfo" 49 | BEGIN 50 | VALUE "Translation", 0x9, 1200 51 | END 52 | END 53 | -------------------------------------------------------------------------------- /resources/ui_icons/sun.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /resources/qml/ModelViews/OrdersListView.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | import QtQuick.Controls 2.12 3 | import QtQuick.Controls.Material 2.12 4 | import CryptoShloma.Gui 1.0 5 | import CryptoShloma.Common 1.0 6 | import CommonLibs 1.0 7 | import BaseElements 1.0 8 | 9 | ListViewStyled { 10 | header: ListViewHeaderStyled { 11 | property var view: ListView.view 12 | width: view.width 13 | Row { 14 | spacing: 1 15 | HeaderLabel { 16 | text: qsTr("Price") 17 | width: 70 18 | } 19 | 20 | HeaderLabel { 21 | text: qsTr("Volume") 22 | width: 110 23 | } 24 | 25 | HeaderLabel { 26 | text: qsTr("Total") 27 | width: 80 28 | } 29 | } 30 | } 31 | 32 | delegate: ListViewDelegateStyled { 33 | property var view: ListView.view 34 | property int row: index 35 | Column { 36 | Row { 37 | spacing: 1 38 | EmptyLabel { 39 | text: CommonLibs.toDouble(model.order.price); 40 | width: 70 41 | } 42 | EmptyLabel { 43 | text: CommonLibs.toDouble(model.order.currencyVolume); 44 | width: 100 45 | } 46 | EmptyLabel { 47 | text: CommonLibs.toDouble(model.order.baseVolume); 48 | width: 80 49 | } 50 | } 51 | 52 | Separator {} 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/algo/i_analytics_algo.cpp: -------------------------------------------------------------------------------- 1 | #include "i_analytics_algo.h" 2 | #include "i_trade_algo.h" 3 | #include 4 | 5 | #include "trend_searcher.h" 6 | #include 7 | 8 | AbstractAnalyticsAlgorithm::AbstractAnalyticsAlgorithm(QObject *parent) noexcept : 9 | QObject(parent) 10 | { 11 | connect(this, &AbstractAnalyticsAlgorithm::ready, this, &AbstractAnalyticsAlgorithm::start); 12 | } 13 | 14 | AbstractAnalyticsAlgorithm::Ptr AbstractAnalyticsAlgorithm::createAlgo(const AnalyticsAlgoType type) noexcept 15 | { 16 | switch (type) { 17 | case TrendSearcherAlgo: 18 | return QSharedPointer::create(); 19 | } 20 | return nullptr; 21 | } 22 | 23 | void AbstractAnalyticsAlgorithm::run() noexcept 24 | { 25 | emit ready(); 26 | } 27 | 28 | void AbstractAnalyticsAlgorithm::setTradeAlgo(const QSharedPointer &algo) noexcept 29 | { 30 | m_TradeAlgo = algo; 31 | 32 | auto marketsStorage = m_TradeAlgo->api()->marketsStorage(); 33 | assert(marketsStorage); 34 | connect(this, &AbstractAnalyticsAlgorithm::updateMarketConfig, marketsStorage, &MarketsStorage::updateMarketUserConfig, Qt::QueuedConnection); 35 | 36 | connect(this, &AbstractAnalyticsAlgorithm::cancelOrderId, m_TradeAlgo->api()->controller().get(), &IExchangeApiController::cancelOrder); 37 | connect(this, &AbstractAnalyticsAlgorithm::submitSellOrderToBuyPrice, m_TradeAlgo->api()->controller().get(), &IExchangeApiController::submitOrder); 38 | } 39 | 40 | QSharedPointer AbstractAnalyticsAlgorithm::tradeAlgo() const 41 | { 42 | return m_TradeAlgo; 43 | } 44 | -------------------------------------------------------------------------------- /src/network/http_client.h: -------------------------------------------------------------------------------- 1 | #ifndef WEBCLIENT_H 2 | #define WEBCLIENT_H 3 | #include 4 | 5 | class QSslError; 6 | class QAuthenticator; 7 | class QNetworkReply; 8 | class QNetworkSession; 9 | 10 | class WebClient final : public QObject 11 | { 12 | Q_OBJECT 13 | public: 14 | explicit WebClient (QObject *parent = nullptr) noexcept; 15 | explicit WebClient(const WebClient &o) noexcept = delete; 16 | WebClient& operator=(const WebClient &o) noexcept = delete; 17 | ~WebClient() override; 18 | QNetworkReply * head(const QNetworkRequest &request) noexcept; 19 | QNetworkReply * get(const QNetworkRequest &request) noexcept; 20 | QNetworkReply * post(const QNetworkRequest &request, const QByteArray &data) noexcept; 21 | QNetworkReply * put(const QNetworkRequest &request, const QByteArray &data) noexcept; 22 | QNetworkReply * del(const QNetworkRequest &request) noexcept; 23 | inline quint32 requestCount() const noexcept { 24 | return m_requestCount; 25 | } 26 | private slots: 27 | void slotAuthenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator) noexcept; 28 | #ifndef QT_NO_SSL 29 | void sslErrors(QNetworkReply *, const QList &errors) noexcept; 30 | #endif 31 | void timerEvent(QTimerEvent *event) noexcept final; 32 | private: 33 | inline void incRequestCount() noexcept { 34 | m_requestCount++; 35 | } 36 | bool isOnline(); 37 | QNetworkAccessManager m_NetworkAccessManager{}; 38 | QNetworkSession *m_session = nullptr; 39 | quint32 m_requestCount{0}; 40 | qint32 m_GlobalRequestCountTimerId{0}; 41 | }; 42 | 43 | #endif // WEBCLIENT_H 44 | -------------------------------------------------------------------------------- /include/init_logger.h: -------------------------------------------------------------------------------- 1 | #ifndef INIT_LOGGER_H 2 | #define INIT_LOGGER_H 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | void initLogger(QCoreApplication *app, const Logger::LogLevel level) 12 | { 13 | #ifndef QT_NO_DEBUG 14 | const QString logDirName{app->applicationDirPath() + QStringLiteral("/logs")}; 15 | #else 16 | const QString logDirName{QStandardPaths::writableLocation(QStandardPaths::HomeLocation) + QString(QStringLiteral("/.%1/logs")).arg(GLOBAL_APP_NAME)}; 17 | #endif 18 | QDir logDir{logDirName}; 19 | if ( !logDir.exists() ) 20 | logDir.mkpath(logDirName); 21 | 22 | const QString logFormat{QStringLiteral("[%{time}{dd-MM-yyyy HH:mm:ss}] [%{type}] <%{function}:%{line}> %{message}\n")}; 23 | const QString &fileFormat = QString("%1/%2.log") 24 | .arg(logDirName) 25 | .arg(app->applicationName()); 26 | #ifndef QT_NO_DEBUG 27 | ConsoleAppender *consoleAppender = new ConsoleAppender(); 28 | consoleAppender->setFormat(logFormat); 29 | consoleAppender->setDetailsLevel(level); 30 | cuteLogger->registerAppender(consoleAppender); 31 | #endif 32 | RollingFileAppender *rollingFileAppender = new RollingFileAppender(fileFormat); 33 | rollingFileAppender->setFormat(logFormat); 34 | rollingFileAppender->setDetailsLevel(level); 35 | rollingFileAppender->setDatePattern(RollingFileAppender::DailyRollover); 36 | cuteLogger->registerAppender(rollingFileAppender); 37 | } 38 | #endif // INIT_LOGGER_H 39 | -------------------------------------------------------------------------------- /src/models/markets_model.h: -------------------------------------------------------------------------------- 1 | #ifndef MARKETSMODEL_H 2 | #define MARKETSMODEL_H 3 | #include 4 | #include 5 | 6 | class MarketsModel : public QAbstractListModel 7 | { 8 | Q_OBJECT 9 | public: 10 | enum MarketsModelRoles { 11 | MarketRole = Qt::UserRole + 1, 12 | IsTradableRole 13 | }; 14 | 15 | explicit MarketsModel(QObject *parent = nullptr) noexcept; 16 | const Market::ConstPtr findMarketById(const Market::MarketId &id) const noexcept; 17 | const Currency::ConstPtr findCurrencyById(const Currency::CurrencyId &id) const noexcept; 18 | private: 19 | int rowCount(const QModelIndex &parent = QModelIndex()) const noexcept override; 20 | QVariant data(const QModelIndex &index, int role = Qt::UserRole) const noexcept override; 21 | bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) noexcept override; 22 | QHash roleNames() const noexcept override; 23 | public slots: 24 | void mergeCurrencies(const QList &data) noexcept; 25 | void mergeMarketInfo(const QList &data) noexcept; 26 | void mergeMarketData(const QList &data) noexcept; 27 | void refresh() noexcept; 28 | void updateMarketConfig(const Market::MarketId &marketId, const Market::UserConfig &config) noexcept; 29 | signals: 30 | void removeTradePairIdFromTrading(const Market::MarketId &id); 31 | void mergeCompeleted(); 32 | private slots: 33 | void cancelTradingMarket(const Market::MarketId &id) noexcept; 34 | private: 35 | Market::Container m_Data{}; 36 | Currency::Container m_Currencys{}; 37 | }; 38 | #endif // MARKETSMODEL_H 39 | -------------------------------------------------------------------------------- /launcher/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.11) 2 | 3 | project(Launcher LANGUAGES CXX) 4 | set(TARGET_NAME ${PROJECT_NAME}) 5 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 6 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 7 | set(CMAKE_AUTOMOC ON) 8 | set(CMAKE_CXX_STANDARD 17) 9 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 10 | 11 | include(../cmake/utils.cmake) 12 | enable_all_warnings() 13 | 14 | find_package(Qt5 5.12.10 REQUIRED COMPONENTS Core) 15 | file(GLOB_RECURSE HEADER_FILES "*.h") 16 | file(GLOB_RECURSE SOURCE_FILES "*.cpp" ) 17 | 18 | if(WIN32) 19 | include(../cmake/generate_product_version.cmake) 20 | 21 | get_filename_component(ICON_PATH 22 | "${CMAKE_CURRENT_SOURCE_DIR}/../resources/ui_icons/rabbi.ico" 23 | ABSOLUTE) 24 | 25 | generate_product_version( 26 | VersionFilesOutputVariableLauncher 27 | NAME "${PROJECT_NAME}" 28 | ICON "${ICON_PATH}" 29 | VERSION_MAJOR ${${PROJECT_NAME}_VERSION_MAJOR} 30 | VERSION_MINOR ${${PROJECT_NAME}_VERSION_MINOR} 31 | VERSION_PATCH ${${PROJECT_NAME}_VERSION_PATCH} 32 | COMPANY_COPYRIGHT "https://github.com/man-k28/CryptoShloma" 33 | ) 34 | add_executable(${PROJECT_NAME} WIN32 ${HEADER_FILES} ${SOURCE_FILES} ${VersionFilesOutputVariableLauncher}) 35 | target_link_options(${TARGET_NAME} PRIVATE -mwindows) 36 | else() 37 | add_executable(${PROJECT_NAME} ${HEADER_FILES} ${SOURCE_FILES}) 38 | endif() 39 | target_compile_definitions(${PROJECT_NAME} PRIVATE $<$,$>:QT_QML_DEBUG>) 40 | target_include_directories(${PROJECT_NAME} PRIVATE ../src ../include) 41 | target_link_libraries(${PROJECT_NAME} PRIVATE 42 | Qt5::Core 43 | CuteLogger 44 | ) 45 | -------------------------------------------------------------------------------- /src/models/buy_orders_proxy_model.cpp: -------------------------------------------------------------------------------- 1 | #include "buy_orders_proxy_model.h" 2 | #include "market_orders_model.h" 3 | 4 | BuyOrdersProxyModel::BuyOrdersProxyModel(QObject *parent) noexcept : 5 | QSortFilterProxyModel(parent) 6 | { 7 | setDynamicSortFilter(true); 8 | sort(0, Qt::DescendingOrder); 9 | } 10 | 11 | //void BuyOrdersProxyModel::refresh() noexcept 12 | //{ 13 | // qobject_cast(sourceModel())->refresh(); 14 | //} 15 | 16 | bool BuyOrdersProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const noexcept 17 | { 18 | const auto & leftVariant = sourceModel()->data( left, MarketOrdersModel::OrderRole ); 19 | const auto & rightVariant = sourceModel()->data( right, MarketOrdersModel::OrderRole ); 20 | Q_ASSERT_X(leftVariant.canConvert< Order * >() && rightVariant.canConvert< Order * >(), "QVariant convert", "BuyOrdersProxyModel::lessThan" ); 21 | 22 | const auto & leftOrder = leftVariant.value< Order * >(); 23 | const auto & rightOrder = rightVariant.value< Order * >(); 24 | 25 | return leftOrder->getPrice() < rightOrder->getPrice(); //FIXME: double compare 26 | } 27 | 28 | bool BuyOrdersProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const noexcept 29 | { 30 | const QModelIndex &buyTypeIndex = sourceModel()->index(sourceRow, 0, sourceParent); 31 | if ( !buyTypeIndex.isValid() ) { 32 | return false; 33 | } 34 | const auto &variant = sourceModel()->data( buyTypeIndex, MarketOrdersModel::OrderRole ); 35 | Q_ASSERT_X(variant.canConvert< Order * >(), "QVariant convert", "BuyOrdersProxyModel::filterAcceptsRow" ); 36 | 37 | const auto &order = variant.value< Order * >(); 38 | 39 | return ( order->getType() == Order::Buy); 40 | } 41 | -------------------------------------------------------------------------------- /src/models/sell_orders_proxy_model.cpp: -------------------------------------------------------------------------------- 1 | #include "sell_orders_proxy_model.h" 2 | #include "market_orders_model.h" 3 | 4 | SellOrdersProxyModel::SellOrdersProxyModel(QObject *parent) noexcept : 5 | QSortFilterProxyModel(parent) 6 | { 7 | setDynamicSortFilter(true); 8 | sort(0, Qt::AscendingOrder); 9 | } 10 | 11 | //void SellOrdersProxyModel::refresh() noexcept 12 | //{ 13 | // qobject_cast(sourceModel())->refresh(); 14 | //} 15 | 16 | bool SellOrdersProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const noexcept 17 | { 18 | const auto & leftVariant = sourceModel()->data( left, MarketOrdersModel::OrderRole ); 19 | const auto & rightVariant = sourceModel()->data( right, MarketOrdersModel::OrderRole ); 20 | Q_ASSERT_X(leftVariant.canConvert< Order * >() && rightVariant.canConvert< Order * >(), "QVariant convert", "SellOrdersProxyModel::lessThan" ); 21 | 22 | const auto & leftOrder = leftVariant.value< Order * >(); 23 | const auto & rightOrder = rightVariant.value< Order * >(); 24 | 25 | return leftOrder->getPrice() < rightOrder->getPrice(); //FIXME: double compare 26 | } 27 | 28 | bool SellOrdersProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const noexcept 29 | { 30 | const QModelIndex &sellTypeIndex = sourceModel()->index(sourceRow, 0, sourceParent); 31 | if ( !sellTypeIndex.isValid() ) { 32 | return false; 33 | } 34 | const auto &variant = sourceModel()->data( sellTypeIndex, MarketOrdersModel::OrderRole ); 35 | Q_ASSERT_X(variant.canConvert< Order * >(), "QVariant convert", "SellOrdersProxyModel::filterAcceptsRow" ); 36 | 37 | const auto &order = variant.value< Order * >(); 38 | 39 | return ( order->getType() == Order::Sell ); 40 | } 41 | -------------------------------------------------------------------------------- /resources/qml/BaseElements/BusyIndicatorStyled.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | import QtQuick.Controls 2.12 3 | import QtQuick.Controls.Material 2.12 4 | 5 | BusyIndicator { 6 | anchors.horizontalCenter: parent.horizontalCenter 7 | anchors.verticalCenter: parent.verticalCenter 8 | id: indicator 9 | 10 | contentItem: Item { 11 | id: item 12 | x: parent.width / 2 - 32 13 | y: parent.height / 2 - 32 14 | implicitWidth: 64 15 | implicitHeight: 64 16 | width: 64 17 | height: 64 18 | opacity: indicator.running ? 1 : 0 19 | 20 | Behavior on opacity { 21 | OpacityAnimator { 22 | duration: 250 23 | } 24 | } 25 | 26 | RotationAnimator { 27 | target: item 28 | running: indicator.visible && indicator.running 29 | from: 0 30 | to: 360 31 | loops: Animation.Infinite 32 | duration: 1250 33 | } 34 | 35 | Repeater { 36 | id: repeater 37 | model: 6 38 | 39 | Rectangle { 40 | x: item.width / 2 - width / 2 41 | y: item.height / 2 - height / 2 42 | implicitWidth: 10 43 | implicitHeight: 10 44 | radius: 5 45 | color: Material.color(Material.Green) 46 | transform: [ 47 | Translate { 48 | y: -Math.min(item.width, item.height) * 0.5 + 5 49 | }, 50 | Rotation { 51 | angle: index / repeater.count * 360 52 | origin.x: 5 53 | origin.y: 5 54 | } 55 | ] 56 | } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/algo/hanukkah.h: -------------------------------------------------------------------------------- 1 | #ifndef HANUKKAH_H 2 | #define HANUKKAH_H 3 | #include "i_trade_algo.h" 4 | #include "hanukkah/i_step.h" 5 | #include 6 | #include 7 | 8 | class Hanukkah; 9 | #pragma pack (push, 1) 10 | class Hanukkah final: public ITradeAlgorithm 11 | { 12 | Q_OBJECT 13 | public: 14 | explicit Hanukkah(QObject *parent = nullptr) noexcept; 15 | ~Hanukkah() noexcept override; 16 | void unlockAllSteps(const Market::MarketId &id) noexcept; 17 | private slots: 18 | bool start() noexcept override final; 19 | bool stop() noexcept override final; 20 | bool restart() noexcept override final; 21 | void reconfigure() noexcept override final; 22 | void timerEvent(QTimerEvent *event) noexcept override final; 23 | void removeMarket(const Market::MarketId &id) noexcept override final; 24 | private: 25 | void execLogic() noexcept; 26 | private: 27 | class StepGenerator 28 | { 29 | public: 30 | explicit StepGenerator() = default; 31 | explicit StepGenerator(const QJsonArray &data, Hanukkah * const parentAlgo, const Market::ConstPtr &market) noexcept; 32 | explicit StepGenerator(const StepGenerator &o) noexcept; 33 | StepGenerator& operator=(const StepGenerator &o) noexcept; 34 | bool update(const QJsonArray &data) noexcept; 35 | QJsonArray metaConfig = {}; 36 | IStep::StepContainer container = {}; 37 | Market::ConstPtr market() const; 38 | private: 39 | void setup() noexcept; 40 | Hanukkah * m_parentAlgo = nullptr; 41 | Market::ConstPtr m_market = {}; 42 | }; 43 | 44 | using SessionContainer = QMap; 45 | SessionContainer m_sessionContainer = {}; 46 | qint32 m_timerId = 0; 47 | }; 48 | #pragma pack (pop) 49 | 50 | #endif // HANUKKAH_H 51 | -------------------------------------------------------------------------------- /src/storage/markets_storage.h: -------------------------------------------------------------------------------- 1 | #ifndef MARKETS_STORAGE_H 2 | #define MARKETS_STORAGE_H 3 | 4 | #include 5 | #include 6 | class MarketsStorage final : public QObject 7 | { 8 | Q_OBJECT 9 | public: 10 | explicit MarketsStorage(QObject *parent = nullptr) noexcept; 11 | public: 12 | const Currency::ConstPtr findCurrencyById(const Currency::CurrencyId &id) const noexcept; 13 | const Currency::ConstPtr findCurrencyBySymbol(const QString &symbol) const noexcept; 14 | const Market::ConstPtr findMarketById(const Market::MarketId &id) const noexcept; 15 | const Market::ConstPtr findMarketByCurrencyId(const Currency::CurrencyId &id) const noexcept; 16 | const Market::ConstPtr findMarketBySymbol(const QString &symbol) const noexcept; 17 | const Market::ConstPtr findMarketByLabel(const QString &label) const noexcept; 18 | const Market::MarketId findMarketIdBySymbol(const QString &symbol) const noexcept; 19 | 20 | const Market::Container filterTradingPairs() const noexcept; 21 | // quint32 getCurrencysSequential() const noexcept; 22 | // quint32 getMarketsSequential() const noexcept; 23 | public slots: 24 | void mergeCurrencies(const QList &data) noexcept; 25 | void mergeMarketInfo(const QList &data) noexcept; 26 | void mergeMarketData(const QList &data) noexcept; 27 | void updateMarketUserConfig(const Market::MarketId &marketId, const Market::UserConfig &config) noexcept; 28 | signals: 29 | void removeTradePairIdFromTrading(const Market::MarketId &id); 30 | private slots: 31 | void cancelTradingMarket(const Market::MarketId &id) noexcept; 32 | private: 33 | Market::Container m_Data{}; 34 | Currency::Container m_Currencys{}; 35 | QReadWriteLock lock{}; 36 | }; 37 | 38 | #endif // MARKETS_STORAGE_H 39 | -------------------------------------------------------------------------------- /src/api/temporary_orders_table.h: -------------------------------------------------------------------------------- 1 | #ifndef TEMPORARYORDERSTABLE_H 2 | #define TEMPORARYORDERSTABLE_H 3 | 4 | #include 5 | 6 | class TemporaryOrdersTable final : public QObject 7 | { 8 | Q_OBJECT 9 | public: 10 | struct OrderRecord { 11 | /*! 12 | * \brief For create new 13 | */ 14 | explicit OrderRecord(const Market::MarketId &marketId, 15 | Order::OrderType type, 16 | const qreal &price, 17 | const qreal &baseVolume, 18 | const qreal &volume = 0.) noexcept; 19 | /*! 20 | * \brief For update exist 21 | */ 22 | explicit OrderRecord(const Market::MarketId &marketId, 23 | const Order::OrderId &orderId, 24 | const QDateTime ×tamp, 25 | const qreal &price, 26 | const qreal &baseVolume, 27 | Order::OrderType type, 28 | const qreal &volume) noexcept; 29 | Market::MarketId marketId = {}; 30 | Order::OrderId orderId = {}; 31 | Order::OrderType orderType = Order::Buy; 32 | qreal volume = 0.; 33 | qreal baseVolume = 0.; 34 | qreal price = 0.; 35 | QDateTime timestamp = {}; 36 | }; 37 | public: 38 | explicit TemporaryOrdersTable(QObject *parent = nullptr) noexcept; 39 | bool submitOrder(const OrderRecord &record); 40 | public slots: 41 | void updatePlaceOrder(const OrderRecord &record); 42 | void updateEraseOrder(const OrderRecord &record); //Для данных из submitOrder 43 | void updateEraseOrder(const Order::OrderId &orderId); //Для данных с истории торгов 44 | private: 45 | QList m_data = {}; 46 | }; 47 | 48 | #endif // TEMPORARYORDERSTABLE_H 49 | -------------------------------------------------------------------------------- /resources/ui_icons/settings-1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/models/balances_proxy_model.cpp: -------------------------------------------------------------------------------- 1 | #include "balances_proxy_model.h" 2 | #include "balances_model.h" 3 | 4 | BalancesProxyModel::BalancesProxyModel(QObject *parent) noexcept : 5 | QSortFilterProxyModel(parent) 6 | { 7 | setDynamicSortFilter(true); 8 | sort(0, Qt::AscendingOrder); 9 | } 10 | 11 | //void BalancesProxyModel::refresh() noexcept 12 | //{ 13 | // qobject_cast(sourceModel())->refresh(); 14 | //} 15 | 16 | //void BalancesProxyModel::setFilterMarketId(const Market::MarketId &id) noexcept 17 | //{ 18 | // m_marketId = id; 19 | // invalidateFilter(); 20 | //} 21 | 22 | bool BalancesProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const noexcept 23 | { 24 | const auto & leftVariant = sourceModel()->data( left, BalancesModel::BalanceRole ); 25 | const auto & rightVariant = sourceModel()->data( right, BalancesModel::BalanceRole ); 26 | Q_ASSERT_X(leftVariant.canConvert< Balance * >() && rightVariant.canConvert< Balance * >(), "QVariant convert", "BalancesProxyModel::lessThan" ); 27 | 28 | const auto & leftBalance = leftVariant.value< Balance * >(); 29 | const auto & rightBalance = rightVariant.value< Balance * >(); 30 | 31 | return leftBalance->getCurrency()->getId() < rightBalance->getCurrency()->getId(); 32 | } 33 | 34 | bool BalancesProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const noexcept 35 | { 36 | const QModelIndex &rowIndex = sourceModel()->index(sourceRow, 0, sourceParent); 37 | if ( !rowIndex.isValid() ) { 38 | return false; 39 | } 40 | const auto &variant = sourceModel()->data( rowIndex, BalancesModel::BalanceRole ); 41 | Q_ASSERT_X(variant.canConvert< Balance * >(), "QVariant convert", "BalancesProxyModel::filterAcceptsRow" ); 42 | 43 | const auto &balance = variant.value< Balance * >(); 44 | 45 | return !qFuzzyIsNull(balance->getTotal()); 46 | } 47 | -------------------------------------------------------------------------------- /src/api/i_exchange_api_controller.h: -------------------------------------------------------------------------------- 1 | #ifndef I_EXCHANGE_API_CONTROLLER_H 2 | #define I_EXCHANGE_API_CONTROLLER_H 3 | #include "base/order.h" 4 | class IExchangeAPI; 5 | 6 | class IExchangeApiController : public QObject 7 | { 8 | Q_OBJECT 9 | public: 10 | using Ptr = QSharedPointer; 11 | using ConstPtr = QSharedPointer; 12 | using MarketsGroup = QHash; 13 | public: 14 | explicit IExchangeApiController(IExchangeAPI *api) noexcept 15 | : QObject() 16 | , m_api(api) {} 17 | explicit IExchangeApiController(const IExchangeApiController &o) noexcept = delete; 18 | IExchangeApiController& operator=(const IExchangeApiController &o) noexcept = delete; 19 | ~IExchangeApiController() override = default; 20 | public slots: 21 | virtual void run() noexcept = 0; 22 | virtual void appendMarket(const Market::MarketId &marketId, 23 | const Market::UserConfig &config) = 0; 24 | virtual void appendMarketsGroup(const MarketsGroup &group) = 0; 25 | virtual void removeMarket(const Market::MarketId &marketId) = 0; 26 | virtual void cancelAllOrders() = 0; 27 | virtual void cancelOrder(const Market::MarketId &marketId, 28 | const Order::OrderId &orderId) = 0; 29 | virtual void submitOrder(const Market::MarketId &marketId, 30 | const qreal &price, 31 | const qreal ¤cyVolume) = 0; 32 | virtual void dropCoin(const Currency::CurrencyId ¤cyId) = 0; 33 | signals: 34 | void appendedMarket(const Market::MarketId &); 35 | void removedMarket(const Market::MarketId &); 36 | void updateMarketConfig(const Market::MarketId &id, const Market::UserConfig &config); 37 | protected: 38 | IExchangeAPI * m_api; 39 | }; 40 | 41 | #endif // I_EXCHANGE_API_CONTROLLER_H 42 | -------------------------------------------------------------------------------- /src/common/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILS_H 2 | #define UTILS_H 3 | #include 4 | #include 5 | 6 | namespace CryptoShloma { 7 | namespace Utils { 8 | inline QString toDoublePrec8(const qreal &val) noexcept 9 | { 10 | return QString::number(val, 'f', 8); 11 | } 12 | 13 | inline QString toULongLong(const qreal &val) noexcept //TODO: deprecated 14 | { 15 | return QString::number(static_cast(val)); 16 | } 17 | 18 | inline QString toDoublePrec1(const qreal &val) noexcept //TODO: deprecated 19 | { 20 | return QString::number(val, 'f', 1); 21 | } 22 | 23 | inline qreal toDouble(const QJsonValue &val) noexcept 24 | { 25 | return val.toString().toDouble(); //FIXME: добавлено из-за того, что QJSonValue.toDouble() теряет точность при уже при 7 знаках, может баг Qt 26 | } 27 | 28 | inline qreal roundingFloor(const qreal &value, const qreal &rounding) 29 | { 30 | Q_ASSERT_X(!qFuzzyIsNull(value), "devide", "Devision by zero"); 31 | const qreal devide = rounding / value; 32 | const qreal round = std::floor(devide); 33 | const qreal ret = round * value; 34 | return ret; 35 | } 36 | 37 | inline qreal roundingCeil(const qreal &value, const qreal &rounding) 38 | { 39 | Q_ASSERT_X(!qFuzzyIsNull(value), "devide", "Devision by zero"); 40 | const qreal devide = rounding / value; 41 | const qreal round = std::ceil(devide); 42 | const qreal ret = round * value; 43 | return ret; 44 | } 45 | 46 | inline qreal rounding(const qreal &value, const qreal &rounding) 47 | { 48 | Q_ASSERT_X(!qFuzzyIsNull(value), "devide", "Devision by zero"); 49 | qint64 round = static_cast((rounding * std::pow(10,8)) / (value * std::pow(10,8))); 50 | return static_cast(round )* value; 51 | } 52 | } 53 | } 54 | #endif // UTILS_H 55 | -------------------------------------------------------------------------------- /src/algo/spreder.h: -------------------------------------------------------------------------------- 1 | #ifndef SPREDER_H 2 | #define SPREDER_H 3 | #include "i_trade_algo.h" 4 | #include 5 | 6 | class Spreder final: public ITradeAlgorithm 7 | { 8 | Q_OBJECT 9 | public: 10 | explicit Spreder(QObject *parent = nullptr) noexcept; 11 | ~Spreder() override = default; 12 | private slots: 13 | bool start() noexcept override final; 14 | bool stop() noexcept override final; 15 | bool restart() noexcept override final; 16 | void reconfigure() noexcept override final; 17 | void timerEvent(QTimerEvent *event) noexcept final; 18 | private: 19 | void startSubmitOrders(const Market::ConstPtr &market, const bool sellMode); 20 | void dropCoin(const Market::ConstPtr &market); 21 | 22 | struct Historydata { 23 | qreal amountBtc{0.}; 24 | qreal rate{0.}; 25 | qreal fee{0.}; 26 | qreal amountCurrency{0.}; 27 | }; 28 | 29 | qreal calculateProfit(const qreal &bid, const qreal &ask, const qreal &fee) const; 30 | qreal calculateStopLoss(const qreal &oldBid, 31 | const qreal &oldFee, 32 | const qreal &oldAmountBtc, 33 | const qreal &fee, 34 | const qreal &oldAmountCurrency, 35 | const qreal &pairProfit) const; 36 | qreal calculateBuyIn(const qreal &oldBid, 37 | const qreal &oldFee, 38 | const qreal &fee, 39 | const qreal &oldAmountCurrency, 40 | const qreal &pairProfit, 41 | const qreal &step) const; 42 | 43 | Historydata findPreviousBuyOrders(const Order::ConstContainer &container) const noexcept; 44 | QPair getStepMerchant(const qreal &trend) const; 45 | void searchAndCancelOrders(const Order::ConstContainer &container) noexcept; 46 | }; 47 | 48 | #endif // SPREDER_H 49 | -------------------------------------------------------------------------------- /resources/ui_icons/store.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /resources/ui_icons/bitcoin.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /.github/workflows/cmake.yml: -------------------------------------------------------------------------------- 1 | name: CMake 2 | 3 | on: [workflow_dispatch] 4 | 5 | env: 6 | # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) 7 | BUILD_TYPE: Release 8 | 9 | jobs: 10 | build: 11 | # The CMake configure and build commands are platform agnostic and should work equally 12 | # well on Windows or Mac. You can convert this to a matrix build if you need 13 | # cross-platform coverage. 14 | # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | 20 | - name: Create Build Environment 21 | # Some projects don't allow in-source building, so create a separate build directory 22 | # We'll use this as our working directory for all subsequent commands 23 | #run: cmake -E make_directory ${{runner.workspace}}/build 24 | 25 | #- name: Configure CMake 26 | # Use a bash shell so we can use the same syntax for environment variable 27 | # access regardless of the host operating system 28 | #shell: bash 29 | #working-directory: ${{runner.workspace}}/build 30 | # Note the current convention is to use the -S and -B options here to specify source 31 | # and build directories, but this is only available with CMake 3.13 and higher. 32 | # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 33 | run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE 34 | 35 | - name: Build 36 | #working-directory: ${{runner.workspace}}/build 37 | #shell: bash 38 | # Execute the build. You can specify a specific target with "--target " 39 | run: cmake --build . --config $BUILD_TYPE 40 | 41 | #- name: Test 42 | # working-directory: ${{runner.workspace}}/build 43 | # shell: bash 44 | # # Execute tests defined by the CMake configuration. 45 | # # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail 46 | # run: ctest -C $BUILD_TYPE 47 | -------------------------------------------------------------------------------- /src/models/spread_markets_proxy_model.cpp: -------------------------------------------------------------------------------- 1 | #include "spread_markets_proxy_model.h" 2 | #include "markets_model.h" 3 | #include 4 | 5 | SpreadMarketsProxyModel::SpreadMarketsProxyModel(QObject *parent) noexcept : 6 | QSortFilterProxyModel(parent) 7 | { 8 | setDynamicSortFilter(true); 9 | sort(0, Qt::DescendingOrder); 10 | } 11 | 12 | bool SpreadMarketsProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const noexcept 13 | { 14 | const auto & leftVariant = sourceModel()->data( left, MarketsModel::MarketRole ); 15 | const auto & rightVariant = sourceModel()->data( right, MarketsModel::MarketRole ); 16 | Q_ASSERT_X(leftVariant.canConvert< Market * >() && rightVariant.canConvert< Market * >(), "QVariant convert", "SpreadMarketsProxyModel::lessThan" ); 17 | 18 | const auto & leftMarket = leftVariant.value< Market * >(); 19 | const auto & rightMarket = rightVariant.value< Market * >(); 20 | 21 | return leftMarket->getSpread() < rightMarket->getSpread(); //FIXME: double compare 22 | } 23 | 24 | bool SpreadMarketsProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const noexcept 25 | { 26 | const QModelIndex &indexRow = sourceModel()->index(sourceRow, 0, sourceParent); 27 | if ( !indexRow.isValid() ) { 28 | return false; 29 | } 30 | const auto &variant = sourceModel()->data( indexRow, MarketsModel::MarketRole ); 31 | Q_ASSERT_X(variant.canConvert< Market * >(), "QVariant convert", "SpreadMarketsProxyModel::filterAcceptsRow" ); 32 | 33 | const auto &market = variant.value< Market * >(); 34 | bool filterText = true; 35 | const auto &pattern = filterRegExp().pattern(); 36 | if ( !pattern.isEmpty() ) 37 | filterText = market->getId().get().contains(pattern, Qt::CaseInsensitive); 38 | 39 | return ( market->getBaseVolume() > SettingsManager::get(SettingsManager::MinBaseVolume).toReal() ) && 40 | ( market->getSpread() > 0 ) && 41 | ( market->getStatus() == Market::Ok ) && 42 | ( !market->getIsTradable() && 43 | filterText); 44 | } 45 | -------------------------------------------------------------------------------- /resources/ui_icons/clock-1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/algo/i_trade_algo.cpp: -------------------------------------------------------------------------------- 1 | #include "i_trade_algo.h" 2 | #include 3 | #include "hanukkah.h" 4 | #include "spreder.h" 5 | #include 6 | #include 7 | 8 | ITradeAlgorithm::ITradeAlgorithm(QObject *parent) noexcept 9 | : QObject(parent) 10 | { 11 | connect(this, &ITradeAlgorithm::ready, this, &ITradeAlgorithm::start, Qt::QueuedConnection); 12 | } 13 | 14 | ITradeAlgorithm::Ptr ITradeAlgorithm::createAlgo(const AlgoType type, const Exchange::Type exchType) noexcept 15 | { 16 | auto api = IExchangeAPI::createExchangeApi(exchType); 17 | api->setAutoDelete(false); 18 | QSharedPointer algo; 19 | switch (type) { 20 | case SprederAlgo: 21 | algo = QSharedPointer::create(); 22 | break; 23 | case HanukkahAlgo: 24 | algo = QSharedPointer::create(); 25 | break; 26 | } 27 | 28 | // QObject::connect(api.get(), &IExchangeAPI::destroyed, algo.get(), [&algo](){algo.clear();}); 29 | algo->setApi(api); 30 | return algo; 31 | } 32 | 33 | void ITradeAlgorithm::run() noexcept 34 | { 35 | QThreadPool::globalInstance()->start(m_Api.get()); 36 | connect(this, &ITradeAlgorithm::ready, m_Api->controller().get(), &IExchangeApiController::run); 37 | emit ready(); 38 | } 39 | 40 | void ITradeAlgorithm::setApi(const QSharedPointer &api) noexcept 41 | { 42 | m_Api = api; 43 | auto marketsStorage = api->marketsStorage(); 44 | assert(marketsStorage); 45 | connect(m_Api->controller().get(), &IExchangeApiController::updateMarketConfig, marketsStorage, &MarketsStorage::updateMarketUserConfig, Qt::QueuedConnection); 46 | connect(m_Api->controller().get(), &IExchangeApiController::removedMarket, this, &ITradeAlgorithm::removeMarket); 47 | connect(this, &ITradeAlgorithm::updateMarketConfig, api->marketsStorage(), &MarketsStorage::updateMarketUserConfig, Qt::QueuedConnection); 48 | } 49 | 50 | void ITradeAlgorithm::init() noexcept 51 | { 52 | connect(m_Api->marketsStorage(), &MarketsStorage::removeTradePairIdFromTrading, m_Api->controller().get(), &IExchangeApiController::removeMarket); 53 | } 54 | -------------------------------------------------------------------------------- /src/models/volume_markets_proxy_model.cpp: -------------------------------------------------------------------------------- 1 | #include "volume_markets_proxy_model.h" 2 | #include "markets_model.h" 3 | #include 4 | 5 | VolumeMarketsProxyModel::VolumeMarketsProxyModel(QObject *parent) noexcept : 6 | QSortFilterProxyModel(parent) 7 | { 8 | setDynamicSortFilter(true); 9 | sort(0, Qt::DescendingOrder); 10 | } 11 | 12 | bool VolumeMarketsProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const noexcept 13 | { 14 | const auto & leftVariant = sourceModel()->data( left, MarketsModel::MarketRole ); 15 | const auto & rightVariant = sourceModel()->data( right, MarketsModel::MarketRole ); 16 | Q_ASSERT_X(leftVariant.canConvert< Market * >() && rightVariant.canConvert< Market * >(), "QVariant convert", "VolumeMarketsProxyModel::lessThan" ); 17 | 18 | const auto & leftMarket = leftVariant.value< Market * >(); 19 | const auto & rightMarket = rightVariant.value< Market * >(); 20 | 21 | return leftMarket->getBaseVolume() < rightMarket->getBaseVolume(); //FIXME: double compare 22 | } 23 | 24 | bool VolumeMarketsProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const noexcept 25 | { 26 | const QModelIndex &indexRow = sourceModel()->index(sourceRow, 0, sourceParent); 27 | if ( !indexRow.isValid() ) { 28 | return false; 29 | } 30 | const auto &variant = sourceModel()->data( indexRow, MarketsModel::MarketRole ); 31 | Q_ASSERT_X(variant.canConvert< Market * >(), "QVariant convert", "VolumeMarketsProxyModel::filterAcceptsRow" ); 32 | const auto &market = variant.value< Market * >(); 33 | bool filterText = true; 34 | const auto &pattern = filterRegExp().pattern(); 35 | if ( !pattern.isEmpty() ) 36 | filterText = market->getId().get().contains(pattern, Qt::CaseInsensitive); 37 | 38 | return ( market->getLastPrice() > SettingsManager::get(SettingsManager::MinCoinPrice).toReal() ) && 39 | ( market->getBaseVolume() > SettingsManager::get(SettingsManager::MinBaseVolume).toReal() ) && 40 | ( market->getStatus() == Market::Ok ) && 41 | ( !market->getIsTradable() && 42 | filterText ); 43 | } 44 | -------------------------------------------------------------------------------- /src/network/websocket_client.h: -------------------------------------------------------------------------------- 1 | #ifndef WEBSOCKETCLIENT_H 2 | #define WEBSOCKETCLIENT_H 3 | 4 | #include 5 | #include 6 | 7 | class QElapsedTimer; 8 | class QSslError; 9 | class QNetworkSession; 10 | class QTimer; 11 | 12 | class WebSocketClient final: public QObject 13 | { 14 | Q_OBJECT 15 | public: 16 | explicit WebSocketClient(QObject *parent = nullptr); 17 | explicit WebSocketClient(const WebSocketClient &o) noexcept = delete; 18 | WebSocketClient& operator=(const WebSocketClient &o) noexcept = delete; 19 | ~WebSocketClient(); 20 | public slots: 21 | void open(); 22 | void close(); 23 | void ping(); 24 | public: 25 | void setBaseTemplate(const QString &baseTempl) noexcept; 26 | void addStreamTemplate(const QString &templ) noexcept; 27 | void removeStreamTemplate(const QString &templ, bool tryReconnect = true) noexcept; 28 | 29 | const QWebSocket *get() noexcept; 30 | bool isConnected() const noexcept; 31 | bool isOpen() const noexcept; 32 | const QNetworkSession * session() noexcept; 33 | private slots: 34 | void onConnected(); 35 | void onDisconnected(); 36 | void onPong(quint64 elapsedTime, const QByteArray &payload); 37 | void onTextMessageReceived(const QString &message); 38 | void onBinaryMessageReceived(const QByteArray &message); 39 | // void onBinaryFrameReceived(const QByteArray &message); 40 | // void onTextFrameReceived(const QString &frame, bool isLastFrame); 41 | void onError(QAbstractSocket::SocketError error); 42 | private slots: 43 | void onNetworkSessionOpened(); 44 | void onNetworkSessionClosed(); 45 | void onNetworkSessionError(QNetworkSession::SessionError error); 46 | #ifndef QT_NO_SSL 47 | void onSslErrors(const QList &errors); 48 | #endif 49 | private: 50 | bool isOnline(); 51 | void reconnect() noexcept; 52 | private: 53 | QWebSocket *m_WebSocket; 54 | QThread *m_thread; 55 | QUrl m_url{}; 56 | QNetworkSession *m_session = nullptr; 57 | QStringList m_streamTemplates{}; 58 | QString m_baseWebSocketTemplate{}; 59 | QTimer *m_pongTimer = nullptr; 60 | }; 61 | 62 | #endif // WEBSOCKETCLIENT_H 63 | -------------------------------------------------------------------------------- /src/boilerplate/singly_linked_stack.h: -------------------------------------------------------------------------------- 1 | #ifndef SINGLY_LINKED_STACK_H 2 | #define SINGLY_LINKED_STACK_H 3 | #include 4 | #include 5 | 6 | template 7 | class SinglyLinkedElement 8 | { 9 | public: 10 | SinglyLinkedElement() = default; 11 | virtual ~SinglyLinkedElement() = default; 12 | void setNext(const Y &next); 13 | inline Y getNext() const 14 | { 15 | return m_next; 16 | } 17 | private: 18 | Y m_next = nullptr; 19 | }; 20 | 21 | template< typename Y > 22 | void SinglyLinkedElement< Y >::setNext(const Y &next) 23 | { 24 | m_next = next; 25 | } 26 | 27 | template 28 | class SinglyLinkedStack final : public QStack 29 | { 30 | public: 31 | SinglyLinkedStack(); 32 | ~SinglyLinkedStack(); 33 | 34 | void insert_back(const T &t); 35 | void remove(); 36 | void setHead(const T &head); 37 | private: 38 | T m_head = nullptr; 39 | }; 40 | 41 | template< typename T > 42 | SinglyLinkedStack< T >::SinglyLinkedStack() : m_head( nullptr ) { 43 | } 44 | 45 | template< typename T > 46 | SinglyLinkedStack< T >::~SinglyLinkedStack() { 47 | while( m_head ) { 48 | remove(); 49 | } 50 | } 51 | 52 | template< typename T > 53 | void SinglyLinkedStack< T >::insert_back( const T &t ) { 54 | // Новый узел привязывается к старому головному элементу 55 | if( !m_head.isNull() ) { 56 | t->setNext(m_head->getNext()); 57 | m_head->setNext(t); 58 | } 59 | // Новый узел сам становится головным элементом 60 | setHead(t); 61 | QStack::push(t); 62 | } 63 | 64 | template< typename T > 65 | void SinglyLinkedStack< T >::remove() { 66 | if( !m_head.isNull() ) { // Если список не пуст: 67 | // Сохраняем указатель на второй узел, который станет новым головным элементом 68 | auto newHead = m_head->getNext(); 69 | QStack::pop(); 70 | m_head.clear(); 71 | 72 | // Назначаем новый головной элемент 73 | m_head = newHead; 74 | } // Иначе могли бы возбудить исключение 75 | } 76 | 77 | template< typename T > 78 | void SinglyLinkedStack< T >::setHead(const T &head) 79 | { 80 | m_head = head; 81 | } 82 | 83 | #endif // SINGLY_LINKED_STACK_H 84 | -------------------------------------------------------------------------------- /resources/ui_icons/big-database.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /cmake/stacktrace.cmake: -------------------------------------------------------------------------------- 1 | option( Use_BreakPad "Use Google BreakPad" OFF ) 2 | if ( Use_BreakPad ) 3 | add_definitions(-DBREAKPAD_ENABLED) 4 | # - Find Breakpad 5 | # 6 | # BREAKPAD_FOUND - True if Breakpad has been found. 7 | # BREAKPAD_INCLUDE_DIRS - Breakpad include directory 8 | # BREAKPAD_LIBRARIES - List of libraries when using Breakpad. 9 | # BREAKPAD_LIBRARY_DIRS - Breakpad library directories 10 | 11 | if(WIN32) 12 | find_path(BREAKPAD_INCLUDE_DIRS 13 | HINTS 14 | "${CMAKE_CURRENT_SOURCE_DIR}/breakpad/prefix/include/breakpad") 15 | 16 | set(BREAKPAD_LIBRARY_NAMES 17 | BREAKPAD_CLIENT 18 | BREAKPAD) 19 | 20 | set(BREAKPAD_LIBRARIES "") 21 | set(BREAKPAD_LIBRARIES_VARS "") 22 | foreach(libname ${BREAKPAD_LIBRARY_NAMES}) 23 | find_library(BREAKPAD_LIBRARY_${libname} 24 | ${libname} 25 | HINTS 26 | "${CMAKE_CURRENT_SOURCE_DIR}/breakpad/prefix/lib") 27 | 28 | list(APPEND BREAKPAD_LIBRARIES ${BREAKPAD_LIBRARY_${libname}}) 29 | list(APPEND BREAKPAD_LIBRARIES_VARS "BREAKPAD_LIBRARY_${libname}") 30 | endforeach() 31 | 32 | set(BREAKPAD_LIBRARY_DIRS "") 33 | else() 34 | set(BREAKPAD_CMAKE_PREFIX_PATH_TEMP ${CMAKE_PREFIX_PATH}) 35 | list(APPEND CMAKE_PREFIX_PATH "${CMAKE_CURRENT_SOURCE_DIR}/breakpad/prefix") 36 | set(ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:${PKG_CONFIG_PATH}") 37 | find_package(PkgConfig REQUIRED) 38 | pkg_search_module(BREAKPAD REQUIRED breakpad-client) 39 | 40 | # reset CMAKE_PREFIX_PATH 41 | set(CMAKE_PREFIX_PATH ${BREAKPAD_CMAKE_PREFIX_PATH_TEMP}) 42 | mark_as_advanced(BREAKPAD_CMAKE_PREFIX_PATH_TEMP) 43 | endif() 44 | 45 | include(FindPackageHandleStandardArgs) 46 | find_package_handle_standard_args(BREAKPAD REQUIRED_VARS BREAKPAD_LIBRARIES BREAKPAD_INCLUDE_DIRS) 47 | 48 | mark_as_advanced(BREAKPAD_LIBRARIES_VARS) 49 | endif() 50 | -------------------------------------------------------------------------------- /resources/qml/TopBar.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | import QtQuick.Controls 2.12 3 | import QtQuick.Controls.Material 2.12 4 | import CryptoShloma.Gui 1.0 5 | import CryptoShloma.Common 1.0 6 | import BaseElements 1.0 7 | 8 | ToolBar { 9 | Material.background: "#303030" 10 | property alias currentIndex: bar.currentIndex 11 | TabBar { 12 | width: parent.width - rightMenu.width 13 | id: bar 14 | Repeater { 15 | model: ExchangePool 16 | 17 | delegate: TabBarIcon { 18 | id: icon 19 | icon.color: model.exchange.api.isOnline ? Material.color(Material.Green) : Material.color(Material.Red) 20 | states: [ 21 | State { 22 | when: model.exchange.api.type == ExchangeType.Binance 23 | PropertyChanges { 24 | target: icon 25 | icon.source: "qrc:/ui/exchange_logos/resources/exchange_logos/binance.svg" 26 | ToolTip.text: qsTr("Binance") 27 | } 28 | } 29 | ] 30 | } 31 | } 32 | } 33 | 34 | Row { 35 | id: rightMenu 36 | anchors.right: parent.right 37 | 38 | ToolBarIcon { 39 | property bool isEnabled: SettingsManager.get(SettingsManager.EnableTestData) === 'true' 40 | icon.source: "qrc:/ui/icons/resources/ui_icons/speedometer.svg" 41 | icon.color: Material.color(Material.Green) 42 | ToolTip.text: qsTr("Open emulator") 43 | onClicked: { 44 | emulatorWindow.dialog.open() 45 | } 46 | visible: isEnabled 47 | } 48 | 49 | ToolBarIcon { 50 | icon.source: "qrc:/ui/icons/resources/ui_icons/big-database.svg" 51 | icon.color: Crud.isOpen ? Material.color(Material.Green) : Material.color(Material.Red) 52 | ToolTip.text: qsTr("Database") 53 | } 54 | 55 | ToolBarIcon { 56 | icon.source: "qrc:/ui/icons/resources/ui_icons/settings.svg" 57 | icon.color: Material.color(Material.Green) 58 | ToolTip.text: qsTr("Settings") 59 | onClicked: settingsWindow.open() 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/storage/trade_history_storage.cpp: -------------------------------------------------------------------------------- 1 | #include "trade_history_storage.h" 2 | #include 3 | #include 4 | #include "markets_storage.h" 5 | 6 | TradeHistoryStorage::TradeHistoryStorage(IExchangeAPI *parent) noexcept 7 | : QObject() 8 | , m_api(parent) 9 | { 10 | } 11 | 12 | Order::ConstContainer TradeHistoryStorage::filter(const Market::MarketId &marketId) const noexcept 13 | { 14 | Order::ConstContainer filtered; 15 | 16 | std::copy_if(m_Data.begin(), m_Data.end(),std::back_inserter(filtered), [&marketId](const Order::Ptr &order){ 17 | return order->getMarket()->getId() == marketId; 18 | }); 19 | std::sort(filtered.begin(), filtered.end(),[](const Order::ConstPtr &leftOrder, const Order::ConstPtr &rightOrder){ 20 | return leftOrder->getTimeStamp() > rightOrder->getTimeStamp(); 21 | }); 22 | return filtered; 23 | } 24 | 25 | void TradeHistoryStorage::insert(const Market::MarketId &id) noexcept 26 | { 27 | QReadLocker locked(&lock); 28 | if ( !m_Filters.contains(id) ) 29 | m_Filters << id; 30 | } 31 | 32 | void TradeHistoryStorage::merge(const QList &data) noexcept 33 | { 34 | LOG_TRACE_TIME(); 35 | QReadLocker locked(&lock); 36 | for (const auto &item : data) { 37 | auto found = std::find_if(m_Data.begin(), m_Data.end(), 38 | [ &item ]( const decltype( m_Data )::value_type &order ) noexcept 39 | { 40 | return order->getOrderId() == item.params.orderId; 41 | }); 42 | if ( found == m_Data.end() ) { 43 | if ( m_Filters.contains(item.marketId) ) { 44 | auto market = m_api->marketsStorage()->findMarketById(item.marketId); 45 | if ( !market.isNull() ) { 46 | auto order = Order::Ptr::create(item.params); 47 | order->setMarket(market); 48 | m_Data.append(order); 49 | } 50 | } 51 | } 52 | } 53 | } 54 | 55 | void TradeHistoryStorage::remove(const Market::MarketId &id) noexcept 56 | { 57 | LOG_TRACE_TIME(); 58 | QReadLocker locked(&lock); 59 | m_Filters.removeAll(id); 60 | for (auto item = m_Data.begin(); item != m_Data.end();) { 61 | if ( item->get()->getMarket()->getId() == id ) 62 | item = m_Data.erase(item); 63 | else 64 | ++item; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/common/settings_manager.h: -------------------------------------------------------------------------------- 1 | #ifndef SETTINGSMANAGER_H 2 | #define SETTINGSMANAGER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "constants.h" 9 | 10 | class QQmlEngine; 11 | class QJSEngine; 12 | 13 | class SettingsManager final: public QObject 14 | { 15 | Q_OBJECT 16 | 17 | static SettingsManager *m_SettingsManager; 18 | explicit SettingsManager(QObject *parent = nullptr) noexcept; 19 | SettingsManager(const SettingsManager &) noexcept = delete; 20 | SettingsManager &operator = (const SettingsManager &) noexcept = delete; 21 | public: 22 | enum Section { 23 | General, 24 | Hanukkah, 25 | Binance 26 | }; 27 | Q_ENUM(Section) 28 | 29 | enum Key { 30 | TradeBaseSumm, 31 | MinSpread, 32 | MinProfit, 33 | MinBaseVolume, 34 | MinCoinPrice, 35 | TradeDifference, //NOTE: нужно для того, что бы выставлять +- от лучших ордеров по рынку 36 | HanukkahTable, 37 | HanukkahMaxSteps, 38 | BinanceEnabled, 39 | BinanceAPIKey, 40 | BinanceSecretKey, 41 | EnableTestData 42 | }; 43 | Q_ENUM(Key) 44 | 45 | static void setDefaults(const QString &str) noexcept; 46 | Q_INVOKABLE static QVariant get(const Key k, const Section s = General) noexcept; 47 | Q_INVOKABLE static void set(const Key k, const Section s = General, const QVariant &data = QVariant()) noexcept; 48 | //Метод с макросом Q_INVOKABLE, возвращающий кастомный тип не работает, должен возвращаться QVariant! 49 | //static ValueRef set(Key, Section s = General); 50 | #ifdef QT_QML_LIB 51 | static QObject *qobject_settingsmanager_provider(QQmlEngine *engine, QJSEngine *scriptEngine) noexcept; 52 | #endif 53 | static SettingsManager *getInstance() noexcept; 54 | static void drop() noexcept; 55 | private: 56 | 57 | QString keyPath(const Section s, const Key k) noexcept; 58 | QMetaEnum m_Keys{}; 59 | QMetaEnum m_Sections{}; 60 | QMap defaults{}; 61 | QMap conf_cache{}; 62 | #ifndef QT_NO_DEBUG 63 | QSettings conf {qApp->applicationDirPath() + "/config.ini", QSettings::IniFormat}; 64 | #else 65 | QSettings conf {QStandardPaths::writableLocation(QStandardPaths::HomeLocation) + QString("/.%1/config.ini").arg(GLOBAL_APP_NAME), QSettings::IniFormat}; 66 | #endif 67 | }; 68 | 69 | #endif // SETTINGSMANAGER_H 70 | -------------------------------------------------------------------------------- /src/base/currency.cpp: -------------------------------------------------------------------------------- 1 | #include "currency.h" 2 | 3 | Currency::Currency(QObject *parent) 4 | : QObject(parent) 5 | , m_config(Config()) 6 | {} 7 | 8 | Currency::Currency(const Currency::Config &config) 9 | { 10 | setId(config.id); 11 | setConfig(config); 12 | } 13 | 14 | Currency::Currency(const Currency &o) 15 | : QObject(o.parent()) 16 | , m_config(o.m_config) 17 | {} 18 | 19 | Currency::Currency(Currency &&o) noexcept 20 | : QObject(o.parent()) 21 | , m_config(std::move(o.m_config)) 22 | {} 23 | 24 | const QString &Currency::getName() const noexcept 25 | { 26 | return m_config.name; 27 | } 28 | 29 | void Currency::setName(const QString &value) noexcept 30 | { 31 | if ( m_config.name != value) { 32 | m_config.name = value; 33 | emit nameChanged(); 34 | } 35 | } 36 | 37 | const QString &Currency::getSymbol() const noexcept 38 | { 39 | return m_config.id.get(); 40 | } 41 | 42 | const QString &Currency::getStatusMessage() const noexcept 43 | { 44 | return m_config.statusMessage; 45 | } 46 | 47 | void Currency::setStatusMessage(const QString &value) noexcept 48 | { 49 | if ( m_config.statusMessage != value ) { 50 | m_config.statusMessage = value; 51 | emit statusChanged(); 52 | } 53 | } 54 | 55 | const Currency::CurrencyId &Currency::getId() const noexcept 56 | { 57 | return m_config.id; 58 | } 59 | 60 | void Currency::setId(const CurrencyId &value) noexcept 61 | { 62 | if ( m_config.id != value ) { 63 | m_config.id = value; 64 | emit idChanged(); 65 | } 66 | } 67 | 68 | Currency::CurrencyStatusListing Currency::getListingStatus() const noexcept 69 | { 70 | return m_config.listingStatus; 71 | } 72 | 73 | void Currency::setListingStatus(const CurrencyStatusListing value) noexcept 74 | { 75 | if ( m_config.listingStatus != value ) { 76 | m_config.listingStatus = value; 77 | emit listingStatusChanged(); 78 | } 79 | } 80 | 81 | Currency::CurrencyStatus Currency::getStatus() const noexcept 82 | { 83 | return m_config.status; 84 | } 85 | 86 | void Currency::setStatus(const CurrencyStatus value) noexcept 87 | { 88 | if ( m_config.status != value ) { 89 | m_config.status = value; 90 | emit statusChanged(); 91 | } 92 | } 93 | 94 | void Currency::setConfig(const Config &config) noexcept 95 | { 96 | setName(config.name); 97 | setStatusMessage(config.statusMessage); 98 | setStatus(config.status); 99 | setListingStatus(config.listingStatus); 100 | } 101 | -------------------------------------------------------------------------------- /src/storage/market_orders_storage.cpp: -------------------------------------------------------------------------------- 1 | #include "market_orders_storage.h" 2 | #include "markets_storage.h" 3 | #include 4 | #include 5 | #include 6 | 7 | MarketOrdersStorage::MarketOrdersStorage(IExchangeAPI *parent) noexcept 8 | : QObject() 9 | , m_api(parent) 10 | { 11 | } 12 | 13 | Order::ConstContainer MarketOrdersStorage::filter(const Market::MarketId &marketId, const Order::OrderType orderType) const noexcept 14 | { 15 | Order::ConstContainer filtered; 16 | 17 | std::copy_if(m_Data.begin(), m_Data.end(),std::back_inserter(filtered), [&marketId, orderType](const Order::Ptr &order){ 18 | return (order->getMarket()->getId() == marketId) && (order->getType() == orderType); 19 | }); 20 | std::sort(filtered.begin(), filtered.end(),[](const Order::ConstPtr &leftOrder, const Order::ConstPtr &rightOrder){ 21 | return leftOrder->getTimeStamp() > rightOrder->getTimeStamp(); 22 | }); 23 | return filtered; 24 | } 25 | 26 | void MarketOrdersStorage::insert(const Market::MarketId &id) noexcept 27 | { 28 | QReadLocker locked(&lock); 29 | if ( !m_Filters.contains(id) ) 30 | m_Filters << id; 31 | } 32 | 33 | void MarketOrdersStorage::merge(const QList &data) noexcept 34 | { 35 | LOG_TRACE_TIME(); 36 | QReadLocker locked(&lock); 37 | QList ids; 38 | for (auto i = data.cbegin(); i != data.cend(); ++i) { 39 | const auto &id = (*i).marketId; 40 | if ( !ids.contains(id) ) 41 | ids << id; 42 | } 43 | for (auto item = m_Data.begin(); item != m_Data.end();) { 44 | if ( ids.contains(item->get()->getMarket()->getId())) 45 | item = m_Data.erase(item); 46 | else 47 | ++item; 48 | } 49 | 50 | for (auto item : data ) { 51 | if ( m_Filters.contains(item.marketId) ) { 52 | auto market = m_api->marketsStorage()->findMarketById(item.marketId); 53 | if ( !market.isNull() ) { 54 | auto order = Order::Ptr::create(item.params); 55 | order->setMarket(market); 56 | m_Data.append(order); 57 | } 58 | } 59 | } 60 | } 61 | 62 | void MarketOrdersStorage::remove(const Market::MarketId &id) noexcept 63 | { 64 | LOG_TRACE_TIME(); 65 | QReadLocker locked(&lock); 66 | m_Filters.removeAll(id); 67 | for (auto item = m_Data.begin(); item != m_Data.end();) { 68 | if ( item->get()->getMarket()->getId() == id ) { 69 | item = m_Data.erase(item); 70 | } else 71 | ++item; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/algo/hanukkah/i_step.h: -------------------------------------------------------------------------------- 1 | #ifndef I_STEP_H 2 | #define I_STEP_H 3 | #include 4 | #include 5 | 6 | class Hanukkah; 7 | class IStep; 8 | using StepPtr = QSharedPointer; 9 | 10 | #pragma pack (push, 1) 11 | class IStep : public SinglyLinkedElement 12 | { 13 | public: 14 | using StepContainer = SinglyLinkedStack; 15 | explicit IStep(const qreal &declinePerc, 16 | const qreal &growth, 17 | Hanukkah * const parentAlgo, 18 | const Market::ConstPtr &market, 19 | bool buyBlocked, 20 | bool sellBlocked) noexcept; 21 | IStep(const IStep &other) noexcept = delete; 22 | IStep & operator=(const IStep &other) noexcept = delete; 23 | 24 | virtual ~IStep(); 25 | 26 | inline qreal getDeclinePerc() const noexcept 27 | { 28 | return m_declinePerc; 29 | } 30 | 31 | inline qreal getGrowth() const noexcept 32 | { 33 | return m_growth; 34 | } 35 | virtual void executeBuyLogic() = 0; 36 | virtual void executeSellLogic() = 0; 37 | inline bool isBuyBlocked() const noexcept 38 | { 39 | return m_isBuyBlocked; 40 | } 41 | inline bool isSellBlocked() const noexcept 42 | { 43 | return m_isSellBlocked; 44 | } 45 | 46 | void setBuyBlocked(bool isBlocked) noexcept; 47 | void setSellBlocked(bool isBlocked) noexcept; 48 | protected: 49 | struct AverageData { 50 | qreal baseVolume{0.}; 51 | qreal price{0.}; 52 | qreal fee{0.}; 53 | qreal volume{0.}; 54 | quint16 ordersCount{0}; 55 | }; 56 | static AverageData calculateAverate(const Order::ConstContainer &container, 57 | const QDateTime &cycleTimeStart) noexcept; 58 | static qreal calculateStopLoss(const qreal &price, 59 | const qreal &oldFee, 60 | const qreal &baseVolume, 61 | const qreal &fee, 62 | const qreal &volume, 63 | const qreal &profit); 64 | void unlock() noexcept; 65 | void updateMarketMetaConfig(bool isBuyCompleted, const QDateTime &cycleTimeStamp = QDateTime()); 66 | protected: 67 | Hanukkah * const m_parentAlgo = nullptr; 68 | Market::ConstPtr m_market = {}; 69 | private: 70 | qreal m_declinePerc = 0.; 71 | qreal m_growth = 0.; 72 | bool m_isBuyBlocked = false; 73 | bool m_isSellBlocked = false; 74 | }; 75 | #pragma pack (pop) 76 | #endif // I_STEP_H 77 | -------------------------------------------------------------------------------- /resources/qml/ModelViews/OrdersView.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | import QtQuick.Controls 2.12 3 | import QtQuick.Controls.Material 2.12 4 | import BaseElements 1.0 5 | 6 | Item { 7 | property alias buyModel: buyView.model 8 | property alias sellModel: sellView.model 9 | property alias title: title.text 10 | EmptyLabel { 11 | id: helpIndicator 12 | text: qsTr("Please, select trading coin") 13 | visible: false 14 | anchors.horizontalCenter: parent.horizontalCenter 15 | anchors.verticalCenter: parent.verticalCenter 16 | color: Material.color(Material.Grey) 17 | } 18 | 19 | id: itemView 20 | EmptyLabel { 21 | id: title 22 | text: "" 23 | anchors.horizontalCenter: parent.horizontalCenter 24 | } 25 | Row { 26 | spacing: 15 27 | 28 | GroupBox { 29 | spacing: 0 30 | padding: 0 31 | title: qsTr("Sell orders") 32 | label: EmptyLabel { 33 | x: parent.leftPadding 34 | width: parent.availableWidth 35 | text: parent.title 36 | elide: Text.ElideRight 37 | } 38 | background: Rectangle { 39 | color: "transparent" 40 | border.width: 0 41 | } 42 | Column { 43 | OrdersListView { 44 | onCountChanged: { 45 | helpIndicator.visible = (count > 0 ? false : true) 46 | } 47 | busyEnabled: false 48 | id: sellView 49 | height: 205 50 | width: 255 51 | } 52 | } 53 | } 54 | 55 | GroupBox { 56 | spacing: 0 57 | padding: 0 58 | title: qsTr("Buy orders") 59 | label: EmptyLabel { 60 | x: parent.rightPadding 61 | width: parent.availableWidth 62 | text: parent.title 63 | horizontalAlignment: Text.AlignRight 64 | elide: Text.ElideRight 65 | } 66 | background: Rectangle { 67 | color: "transparent" 68 | border.width: 0 69 | } 70 | Column { 71 | OrdersListView { 72 | onCountChanged: { 73 | helpIndicator.visible = (count > 0 ? false : true) 74 | } 75 | busyEnabled: false 76 | id: buyView 77 | height: 205 78 | width: 255 79 | } 80 | } 81 | } 82 | } 83 | } 84 | --------------------------------------------------------------------------------