From a0f5901b03f4bee58a339000aa88c8deacaf626c Mon Sep 17 00:00:00 2001 From: Andre Leibovici Date: Mon, 27 Dec 2021 11:51:36 +1300 Subject: [PATCH] v 1.9 --- algorithms/algorithms.go | 54 ++++++--------- documentation/HowToUse.md | 6 +- exchange/exchange.go | 24 ++++++- logger/logger.go | 14 ++++ main.go | 13 +++- mysql/cryptopump.sql | 99 +++++++++++++++++++++------- mysql/mysql.go | 106 ++++++++++++++++++++---------- static/stylesheets/cryptopump.css | 3 +- templates/index_nostart.html | 28 ++++++-- types/types.go | 1 + 10 files changed, 242 insertions(+), 106 deletions(-) diff --git a/algorithms/algorithms.go b/algorithms/algorithms.go index 01362d3..63b84a1 100644 --- a/algorithms/algorithms.go +++ b/algorithms/algorithms.go @@ -105,22 +105,22 @@ func UpdatePendingOrders( sessionData *types.Session) { var err error - var orderID int64 + var order types.Order var orderStatus *types.Order - if orderID, _, err = mysql.GetOrderTransactionPending(sessionData); err != nil { + if order, err = mysql.GetOrderTransactionPending(sessionData); err != nil { /* Cleanly exit ThreadID */ threads.Thread{}.Terminate(sessionData, functions.GetFunctionName()+" - "+err.Error()) } - if orderID != 0 { + if order.OrderID != 0 { if orderStatus, err = exchange.GetOrder( configData, sessionData, - orderID); err != nil { + int64(order.OrderID)); err != nil { return @@ -263,14 +263,9 @@ func isBuyUpmarket( } /* This function retrieve the next transaction from Thread database and verify that - the ticker price not half profit close to the transaction.This function avoid multiple + the ticker price is not half profit close to the transaction.This function avoid multiple upmarket buy close to each other. */ - if order.OrderID, - order.Price, - order.ExecutedQuantity, - order.CumulativeQuoteQuantity, - order.TransactTime, - err = mysql.GetThreadLastTransaction(sessionData); err != nil { + if order, err = mysql.GetThreadLastTransaction(sessionData); err != nil { sessionData.BuyDecisionTreeResult = "Error" @@ -1072,18 +1067,21 @@ func SellDecisionTree( } - /* Force Sell Most recent open order*/ + /* Check for Force Sell */ if sessionData.ForceSell { - /* Retrieve the last 'active' BUY transaction for a Thread */ - order.OrderID, - order.Price, - order.ExecutedQuantity, - order.CumulativeQuoteQuantity, - order.TransactTime, - _ = mysql.GetThreadLastTransaction(sessionData) + if sessionData.ForceSellOrderID != 0 { /* Force sell a specific orderID */ - return true, order + order, err = mysql.GetOrderByOrderID(sessionData) /* Get order details */ + sessionData.ForceSellOrderID = 0 /* Clear Force sell OrderID */ + return true, order + + } else if sessionData.ForceSellOrderID == 0 { /* Force Sell Most recent open order*/ + + order, err = mysql.GetThreadLastTransaction(sessionData) /* Get order details */ + return true, order + + } } @@ -1114,12 +1112,7 @@ func SellDecisionTree( if (sessionData.SymbolFiatFunds - configData.SymbolFiatStash) < configData.BuyQuantityFiatDown { /* Retrieve the last 'active' BUY transaction for a Thread */ - order.OrderID, - order.Price, - order.ExecutedQuantity, - order.CumulativeQuoteQuantity, - order.TransactTime, - _ = mysql.GetThreadLastTransaction(sessionData) + order, err = mysql.GetThreadLastTransaction(sessionData) if marketData.Price < (order.Price * (1 - configData.BuyRepeatThresholdDown)) { @@ -1157,14 +1150,7 @@ func SellDecisionTree( } /* Retrieve lowest price order from Thread database */ - if order.OrderID, - order.Price, - order.ExecutedQuantity, - order.CumulativeQuoteQuantity, - order.TransactTime, - err = mysql.GetThreadTransactionByPrice( - marketData, - sessionData); err != nil { + if order, err = mysql.GetThreadTransactionByPrice(marketData, sessionData); err != nil { sessionData.SellDecisionTreeResult = "Error" diff --git a/documentation/HowToUse.md b/documentation/HowToUse.md index 64d1901..013ecad 100644 --- a/documentation/HowToUse.md +++ b/documentation/HowToUse.md @@ -104,6 +104,8 @@ The higher the value, the more bullish the market needs to be in order to execut - Diff: this value indicates the difference between current transaction sale price and zero margin sale (includes exchange fee). +- Action [Sell]: The Sell button allows you to execute a sale of an existing order. The 'Diff' lets you know if the order is likely to have a profitable sale or if it is underwater. The sale will occur on the spot market at current market prices. + ## STATUS In the bottom right corner the system status is displayed: @@ -141,9 +143,9 @@ In the bottom right corner the system status is displayed: - Update: write the changes made within the webui into the configuration file. -- Buy market: executes a buy order at the price in that particular moment. +- Buy market: Buy order. The purchase will occur on the spot market at current market prices. -- Sell market: executes a sell order at the price in that particular moment, each press will sell one particular order, press multiple times to sell all. +- Sell market: Sell the top order in the orders table. The sale will occur on the spot market at current market prices. ### TELEGRAM: diff --git a/exchange/exchange.go b/exchange/exchange.go index 2d03a42..7e488fb 100644 --- a/exchange/exchange.go +++ b/exchange/exchange.go @@ -345,6 +345,17 @@ func BuyTicker( /* Exit if DryRun mode set to true */ if configData.DryRun { + logger.LogEntry{ + Config: configData, + Market: marketData, + Session: sessionData, + Order: &types.Order{ + Price: marketData.Price, + }, + Message: "BUYDRYRUN", + LogLevel: "InfoLevel", + }.Do() + return } @@ -519,6 +530,17 @@ func SellTicker( /* Exit if DryRun mode set to true */ if configData.DryRun { + logger.LogEntry{ + Config: configData, + Market: marketData, + Session: sessionData, + Order: &types.Order{ + Price: marketData.Price, + }, + Message: "SELLDRYRUN", + LogLevel: "InfoLevel", + }.Do() + return } @@ -609,7 +631,7 @@ S: int64(orderResponse.OrderID)); err != nil { switch { - case strings.Contains(err.Error(), "-2010"), strings.Contains(err.Error(), "-2011"),strings.Contains(err.Error(), "-1021") : + case strings.Contains(err.Error(), "-2010"), strings.Contains(err.Error(), "-2011"), strings.Contains(err.Error(), "-1021"): /* -2011 Order filled in full before cancelling */ /* -2010 Account has insufficient balance for requested action */ /* -1021 Timestamp for this request was 1000ms ahead of the server's time */ diff --git a/logger/logger.go b/logger/logger.go index d23bced..a02e88d 100644 --- a/logger/logger.go +++ b/logger/logger.go @@ -81,6 +81,13 @@ func (logEntry LogEntry) Do() { "orderPrice": fmt.Sprintf("%.4f", logEntry.Order.Price), }).Info(logEntry.Message) + case "BUYDRYRUN": + + log.WithFields(log.Fields{ + "threadID": logEntry.Session.ThreadID, + "orderPrice": fmt.Sprintf("%.4f", logEntry.Order.Price), + }).Info(logEntry.Message) + case "SELL": log.WithFields(log.Fields{ @@ -90,6 +97,13 @@ func (logEntry LogEntry) Do() { "orderPrice": fmt.Sprintf("%.4f", logEntry.Order.Price), }).Info(logEntry.Message) + case "SELLDRYRUN": + + log.WithFields(log.Fields{ + "threadID": logEntry.Session.ThreadID, + "orderPrice": fmt.Sprintf("%.4f", logEntry.Order.Price), + }).Info(logEntry.Message) + case "CANCELED": if logEntry.Config.Debug { diff --git a/main.go b/main.go index 16f899f..edc383b 100644 --- a/main.go +++ b/main.go @@ -276,7 +276,18 @@ func (fh *myHandler) handler(w http.ResponseWriter, r *http.Request) { case "sell": - fh.sessionData.ForceSell = true /* Force sell */ + if r.PostFormValue("orderID") == "" { /* Check if the orderID is empty */ + + fh.sessionData.ForceSellOrderID = 0 /* Force sell most recent order */ + fh.sessionData.ForceSell = true /* Force sell */ + + } else { + + fh.sessionData.ForceSellOrderID = functions.StrToInt(r.PostFormValue("orderID")) /* Force sell a specific orderID */ + fh.sessionData.ForceSell = true /* Force sell */ + + } + http.Redirect(w, r, fmt.Sprintf(r.URL.Path), 301) /* Redirect to root 'index' */ case "configTemplate": diff --git a/mysql/cryptopump.sql b/mysql/cryptopump.sql index 3e327d7..e05d4f0 100644 --- a/mysql/cryptopump.sql +++ b/mysql/cryptopump.sql @@ -1,10 +1,10 @@ CREATE DATABASE IF NOT EXISTS `cryptopump` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci */ /*!80016 DEFAULT ENCRYPTION='N' */; USE `cryptopump`; --- MySQL dump 10.13 Distrib 8.0.22, for macos10.15 (x86_64) +-- MySQL dump 10.13 Distrib 8.0.27, for macos11 (x86_64) -- -- Host: 127.0.0.1 Database: cryptopump -- ------------------------------------------------------ --- Server version 8.0.23 +-- Server version 8.0.27 /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; @@ -55,7 +55,8 @@ CREATE TABLE `orders` ( `ThreadID` varchar(45) NOT NULL, `ThreadIDSession` varchar(45) NOT NULL, PRIMARY KEY (`OrderID`), - UNIQUE KEY `OrderID_UNIQUE` (`OrderID`) + UNIQUE KEY `OrderID_UNIQUE` (`OrderID`), + KEY `orders_idx_side_status` (`Side`,`Status`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; /*!40101 SET character_set_client = @saved_cs_client */; @@ -77,7 +78,7 @@ CREATE TABLE `session` ( `Status` tinyint(1) NOT NULL, PRIMARY KEY (`ID`), UNIQUE KEY `ThreadID_UNIQUE` (`ThreadID`) -) ENGINE=InnoDB AUTO_INCREMENT=1479 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; +) ENGINE=InnoDB AUTO_INCREMENT=1565 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -96,7 +97,7 @@ CREATE TABLE `thread` ( `Price` float NOT NULL, `ExecutedQuantity` float NOT NULL, PRIMARY KEY (`ID`) -) ENGINE=InnoDB AUTO_INCREMENT=8928 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; +) ENGINE=InnoDB AUTO_INCREMENT=9028 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -254,6 +255,40 @@ DELIMITER ; /*!50003 SET character_set_client = @saved_cs_client */ ; /*!50003 SET character_set_results = @saved_cs_results */ ; /*!50003 SET collation_connection = @saved_col_connection */ ; +/*!50003 DROP PROCEDURE IF EXISTS `GetOrderByOrderID` */; +/*!50003 SET @saved_cs_client = @@character_set_client */ ; +/*!50003 SET @saved_cs_results = @@character_set_results */ ; +/*!50003 SET @saved_col_connection = @@collation_connection */ ; +/*!50003 SET character_set_client = utf8mb4 */ ; +/*!50003 SET character_set_results = utf8mb4 */ ; +/*!50003 SET collation_connection = utf8mb4_0900_ai_ci */ ; +/*!50003 SET @saved_sql_mode = @@sql_mode */ ; +/*!50003 SET sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION' */ ; +DELIMITER ;; +CREATE DEFINER=`root`@`%` PROCEDURE `GetOrderByOrderID`(IN in_param_OrderID bigint, IN in_param_ThreadID varchar(45)) +BEGIN +DECLARE declared_in_param_orderid BIGINT; +DECLARE declared_in_param_threadid CHAR(50); +SET declared_in_param_orderid = in_param_orderid; +SET declared_in_param_threadid = in_param_threadid; +SELECT + `orders`.`orderid` AS `OrderID`, + `orders`.`price` AS `Price`, + `orders`.`executedquantity` AS `ExecutedQuantity`, + `orders`.`cummulativequoteqty` AS `CummulativeQuoteQty`, + `orders`.`transacttime` AS `TransactTime` +FROM + `orders` +WHERE + (`orders`.`orderid` = declared_in_param_orderid + AND `orders`.`threadid` = declared_in_param_threadid) +LIMIT 1; +END ;; +DELIMITER ; +/*!50003 SET sql_mode = @saved_sql_mode */ ; +/*!50003 SET character_set_client = @saved_cs_client */ ; +/*!50003 SET character_set_results = @saved_cs_results */ ; +/*!50003 SET collation_connection = @saved_col_connection */ ; /*!50003 DROP PROCEDURE IF EXISTS `GetOrderSymbol` */; /*!50003 SET @saved_cs_client = @@character_set_client */ ; /*!50003 SET @saved_cs_results = @@character_set_results */ ; @@ -405,13 +440,13 @@ DELIMITER ; DELIMITER ;; CREATE DEFINER=`root`@`%` PROCEDURE `GetProfit`() BEGIN -SELECT - SUM(`source`.`Profit`) AS `profit`, - SUM(`source`.`Profit`) + (`source`.`Diff`) AS `netprofit`, - AVG(`source`.`Percentage`) AS `avg` -FROM - (SELECT - `orders`.`Side` AS `Side`, +SELECT + SUM(`source`.`Profit`) AS `profit`, + SUM(`source`.`Profit`) + (`source`.`Diff`) AS `netprofit`, + AVG(`source`.`Percentage`) AS `avg` + FROM + (SELECT + `orders`.`Side` AS `Side`, `Orders`.`Side` AS `Orders__Side`, `orders`.`Status` AS `Status`, `Orders`.`Status` AS `Orders__Status`, @@ -419,19 +454,33 @@ FROM `Orders`.`CummulativeQuoteQty` AS `Orders__CummulativeQuoteQty`, `orders`.`CummulativeQuoteQty` AS `CummulativeQuoteQty`, (`Orders`.`CummulativeQuoteQty` - `orders`.`CummulativeQuoteQty`) AS `Profit`, - ((`Orders`.`CummulativeQuoteQty` - `orders`.`CummulativeQuoteQty`) / CASE - WHEN `Orders`.`CummulativeQuoteQty` = 0 THEN NULL - ELSE `Orders`.`CummulativeQuoteQty` - END) AS `Percentage`, - (SELECT sum(`session`.`DiffTotal`) AS `sum` FROM `session`) AS `Diff` - FROM - `orders` - INNER JOIN `orders` `Orders` ON `orders`.`OrderID` = `Orders`.`OrderIDSource`) `source` + ((`Orders`.`CummulativeQuoteQty` - `orders`.`CummulativeQuoteQty`) / CASE + WHEN `Orders`.`CummulativeQuoteQty` = 0 THEN NULL + ELSE `Orders`.`CummulativeQuoteQty` END) AS `Percentage`, +(SELECT + sum(`session`.`DiffTotal`) AS `sum` +FROM + `session`) AS `Diff` +FROM +`orders` +INNER JOIN +`orders` `Orders` + ON `orders`.`OrderID` = `Orders`.`OrderIDSource` WHERE - (`source`.`Side` = 'BUY' - AND `source`.`Orders__Side` = 'SELL' - AND `source`.`Status` = 'FILLED' - AND `source`.`Orders__Status` = 'FILLED'); +( + `orders`.`Side` = 'BUY' +) +AND ( + `orders`.`Status` = 'FILLED' +) +) `source` +WHERE +( +1 = 1 +AND `source`.`Orders__Side` = 'SELL' +AND 1 = 1 +AND `source`.`Orders__Status` = 'FILLED' +); END ;; DELIMITER ; /*!50003 SET sql_mode = @saved_sql_mode */ ; @@ -917,4 +966,4 @@ DELIMITER ; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2021-12-08 21:09:38 +-- Dump completed on 2021-12-26 8:08:18 diff --git a/mysql/mysql.go b/mysql/mysql.go index 49f2274..6f878db 100644 --- a/mysql/mysql.go +++ b/mysql/mysql.go @@ -714,7 +714,7 @@ func GetThreadTransactionDistinct( // GetOrderTransactionPending Get 1 order with pending FILLED status func GetOrderTransactionPending( - sessionData *types.Session) (orderID int64, symbol string, err error) { + sessionData *types.Session) (order types.Order, err error) { var rows *sql.Rows @@ -722,36 +722,34 @@ func GetOrderTransactionPending( sessionData.ThreadID); err != nil { logger.LogEntry{ - Config: nil, - Market: nil, - Session: sessionData, - Order: &types.Order{ - OrderID: int(orderID), - }, + Config: nil, + Market: nil, + Session: sessionData, + Order: &types.Order{}, Message: functions.GetFunctionName() + " - " + err.Error(), LogLevel: "DebugLevel", }.Do() - return 0, "", err + return types.Order{}, err } for rows.Next() { err = rows.Scan( - &orderID, - &symbol) + &order.OrderID, + &order.Symbol) } rows.Close() - return orderID, symbol, err + return order, err } -// GetThreadTransactionByPrice function +// GetThreadTransactionByPrice retrieve lowest price order from Thread database func GetThreadTransactionByPrice( marketData *types.Market, - sessionData *types.Session) (orderID int, price float64, executedQuantity float64, cumulativeQuoteQty float64, transactTime int64, err error) { + sessionData *types.Session) (order types.Order, err error) { var rows *sql.Rows @@ -760,32 +758,30 @@ func GetThreadTransactionByPrice( marketData.Price); err != nil { logger.LogEntry{ - Config: nil, - Market: nil, - Session: sessionData, - Order: &types.Order{ - OrderID: int(orderID), - }, + Config: nil, + Market: nil, + Session: sessionData, + Order: &types.Order{}, Message: functions.GetFunctionName() + " - " + err.Error(), LogLevel: "DebugLevel", }.Do() - return 0, 0, 0, 0, 0, err + return types.Order{}, err } for rows.Next() { err = rows.Scan( - &cumulativeQuoteQty, - &orderID, - &price, - &executedQuantity, - &transactTime) + &order.CumulativeQuoteQuantity, + &order.OrderID, + &order.Price, + &order.ExecutedQuantity, + &order.TransactTime) } rows.Close() - return orderID, price, executedQuantity, cumulativeQuoteQty, transactTime, err + return order, err } @@ -829,42 +825,80 @@ func GetThreadTransactionByPriceHigher( } -// GetThreadLastTransaction Return the last 'active' BUY transaction for a Thread +// GetThreadLastTransaction function returns the last BUY transaction for a Thread func GetThreadLastTransaction( - sessionData *types.Session) (orderID int, price float64, executedQuantity float64, cumulativeQuoteQty float64, transactTime int64, err error) { + sessionData *types.Session) (order types.Order, err error) { var rows *sql.Rows if rows, err = sessionData.Db.Query("call cryptopump.GetThreadLastTransaction(?)", sessionData.ThreadID); err != nil { + logger.LogEntry{ + Config: nil, + Market: nil, + Session: sessionData, + Order: &types.Order{}, + Message: functions.GetFunctionName() + " - " + err.Error(), + LogLevel: "DebugLevel", + }.Do() + + return types.Order{}, err + + } + + for rows.Next() { + err = rows.Scan( + &order.CumulativeQuoteQuantity, + &order.OrderID, + &order.Price, + &order.ExecutedQuantity, + &order.TransactTime) + } + + rows.Close() + + return order, err + +} + +// GetOrderByOrderID Return order by OrderID (uses ThreadID as filter) +func GetOrderByOrderID( + sessionData *types.Session) (order types.Order, err error) { + + var rows *sql.Rows + + if rows, err = sessionData.Db.Query("call cryptopump.GetOrderByOrderID(?,?)", + sessionData.ForceSellOrderID, + sessionData.ThreadID); err != nil { + logger.LogEntry{ Config: nil, Market: nil, Session: sessionData, Order: &types.Order{ - OrderID: int(orderID), + OrderID: sessionData.ForceSellOrderID, }, Message: functions.GetFunctionName() + " - " + err.Error(), LogLevel: "DebugLevel", }.Do() - return 0, 0, 0, 0, 0, err + return types.Order{}, err } for rows.Next() { err = rows.Scan( - &cumulativeQuoteQty, - &orderID, - &price, - &executedQuantity, - &transactTime) + &order.OrderID, + &order.Price, + &order.ExecutedQuantity, + &order.CumulativeQuoteQuantity, + &order.TransactTime) } rows.Close() - return orderID, price, executedQuantity, cumulativeQuoteQty, transactTime, err + return order, err } diff --git a/static/stylesheets/cryptopump.css b/static/stylesheets/cryptopump.css index 29d3ea9..639b174 100644 --- a/static/stylesheets/cryptopump.css +++ b/static/stylesheets/cryptopump.css @@ -59,7 +59,8 @@ .table-wrapper { display: inline-block; - margin: 10px; + border: none none 1px; + margin: 0px; max-height: 400px; min-width: 300px; overflow-x: hidden; diff --git a/templates/index_nostart.html b/templates/index_nostart.html index 6b7d774..e13573b 100644 --- a/templates/index_nostart.html +++ b/templates/index_nostart.html @@ -52,17 +52,21 @@ $('#divIDSessionRateCounter').html(json.Session.RateCounter); $('#divIDSessionBuyDecisionTreeResult').html(json.Session.BuyDecisionTreeResult); $('#divIDSessionSellDecisionTreeResult').html(json.Session.SellDecisionTreeResult); - + function buildHtmlTable(selector) { var columns = addAllColumnHeaders(json.Session.Orders, selector); for (var i = 0; i < json.Session.Orders.length; i++) { var row$ = $(''); + var orderID$ = 0; // variable for orderID sale for (var colIndex = 0; colIndex < columns.length; colIndex++) { - var cellValue = json.Session.Orders[i][columns[colIndex]]; - if (cellValue == null) cellValue = ""; - row$.append($('').html(cellValue)); + if (colIndex == 0) {orderID$ = json.Session.Orders[i][columns[colIndex]]}; // variable for orderID sale + var cellValue = json.Session.Orders[i][columns[colIndex]]; + if (cellValue == null) cellValue = ""; + row$.append($('').html(cellValue)); } + var sellButton = $(''); // add sell button to each row in Orders table + row$.append($('').html(sellButton)); $(selector).append(row$); } } @@ -80,6 +84,8 @@ } } } + var sellHeader = $('Action'); // action header to Orders table + headerTr$.append($('').html(sellHeader)); // add action header to Orders table $(selector).append(headerTr$); return columnSet; } @@ -87,7 +93,15 @@ $('#excelDataTable').empty(); buildHtmlTable('#excelDataTable') - }, 1000); + }, 2000); + + /* Submit the form with a specific orderID for sale */ + function OrderSell(OrderID) { + document.getElementById('submitselect').value='sell'; + document.getElementById('orderID').value=OrderID; + document.getElementById("myForm").submit(); + } + @@ -164,6 +178,7 @@
+
{{ .HTMLSnippet }}
@@ -176,9 +191,10 @@
-
+ +
diff --git a/types/types.go b/types/types.go index 514addb..b527d87 100644 --- a/types/types.go +++ b/types/types.go @@ -87,6 +87,7 @@ type Session struct { ConfigTemplate int ForceBuy bool /* This boolean when True force BUY transaction */ ForceSell bool /* This boolean when True force SELL transaction */ + ForceSellOrderID int /* This variable stores the OrderID of ForceSell */ ListenKey string /* Listen key for user stream service */ MasterNode bool /* This boolean is true when Master Node is elected */ TgBotAPI *tgbotapi.BotAPI /* This variable holds Telegram session bot */