diff --git a/README.md b/README.md index 8c62dac..37a99d1 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,8 @@ Always start by running a this trading tool in Dry-run and do not engage money b - CryptoPump has a native Telegram bot that accepts commands /stop /sell /buy and /report. Telegram will also alert you if any issues happen. The Telegram APIKEY, if in use, has to be configured at TGBOTAPIKEY in the config.yml file. +![](https://github.com/aleibovici/img/blob/b2c9390494906b8e83635a5f320dd48f67a48fbd/telegram_screenshot.jpg?raw=true) + - CryptoPump requires MySQL to persist data and transactions, and the .sql file to create the structure can be found in the MySQL folder (cryptopump.sql). I use MySQL with Docker in the same machine Cryptopump is running, and it performs well. Cloud-based MySQL instances are also supported. The environment variables are in launch.json if Visual Studio Code is in use; optionally, the following environment variables set DB_USER, DB_PASS, DB_TCP_HOST, DB_PORT, DB_NAME. For using MySQL with docker go here (). (refer to HOW TO INSTALL file) - To use Binance TestNet, configure APIKEYTESTNET and SECRETKEYTESTNET in config.yml and set the TestNet option to True in the config .yml. Given it requires to be set when starting the code TestNet is disabled in the UI. () diff --git a/documentation/HowToUse.md b/documentation/HowToUse.md index 6f28097..64d1901 100644 --- a/documentation/HowToUse.md +++ b/documentation/HowToUse.md @@ -2,19 +2,32 @@ Cryptopump opens in your browse it's first instance. -On the top left you it shows the thread name and how many instances are running. -Profit shows the profit in the currency set and it's %.* -Deployed show how much fiat currency is used.* -Funds shows the total amount of fiat you have available in the selected trading pair. -Transact./h shows how many transaction the bot did per hour. +### METRICS -*If more than one instance is running the total amount will be updated. +- On the top left you it shows the thread name and how many instances are in execution. + +- Profit: Shows the Total Profit (Total Profit = Sales - Buys); the Net Profit (Net Profit = Total Profit - Order Differences) where Order Difference is the total difference between each order price and the current pair price for all threads. Another way to understand Net Profit is to look at is as the total profit if all orders were to be closed at that moment in time. Net profit is important because CryptoPump will use Profits to buy orders if the crypto pair goes down in price; finally, the average transaction percentage profit across all present and past running threads. + +- Thread Profit: Shows the ToNet Profit (Net Thread Profit = Total Thread Profit - Order Thread Differences) where Order Difference is the total difference between each order price and the current pair price for the current threads.; finally, the average transaction percentage profit across the running thread. + +- Diff: Shows the sum of Order Differences for the current thread. Order Difference is the total difference between each order price and the current pair price for the current threads. + +- Deployed: Shows how much fiat currency is in use across all threads. + +- Funds: Shows the total amount of crypto pairs acquired by the current thread and the amount of FIAT currency available for additional purchases. + +- Offset : In rare circumstances the database may become out-of-sync with the amount of crypto invested due to the Exchange or Connectivity error. This field represent the disparity between the system and the exchange quantities. (0 means no difference and all is good) + +- Transact./h: Number of Sale transactions per hour. + +- MACD is the Moving Average Convergence Divergence it is a trend-following momentum indicator that shows the relationship between two moving averages. + +- RSI 14/7/3 is the Relative Strength Index it's an indicator based on closing prices over a duration of specific time. + +- Direction: Updated every second from the exchange and is increased at each movement in the same direction, i.e. if the price moves up 10 consecutive times then the direction will be 10. + +- Price$: Current price of the selected crypto currency. -On the top right you can see indicators for that particular trading pair. -MACD is the Moving Average Convergence Divergence it is a trend-following momentum indicator that shows the relationship between two moving averages. -RSI 14/7/3 is the Relative Strength Index it's an indicator based on closing prices over a duration of specific time. -Direction is updated every second from your exchange and is greater at each movement in the same direction, i.e. if the price moves up 10 consecutive times it will show as 10 under direction. -Price$ is the cost of the selected currency. ## SETTING UP @@ -77,7 +90,7 @@ The higher the value, the more bullish the market needs to be in order to execut - Stop Time: If enforce time is set to true this value is used to stop the bot operation. -### UI GRID +### ORDERS GRID - OrderID: this value is provided by the exchange when a buy order takes place. @@ -91,6 +104,18 @@ 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). +## STATUS + +In the bottom right corner the system status is displayed: + +- Buy: Reason in the decision tree on why a given Buy order is not being executed. This field is important and provide information on what configuration tunning might be required. + +- Sell: Reason in the decision tree on why a given Sell order is not being executed. This field is important and provide information on what configuration tunning might be required. + +- Ops/dec: Number of operation per second. This number is dictated by the crypto-pair volume. Cryptopump analyses every Exchange kline block. + +- Signal: Average latency between Cryptopump and the exchange measured every five seconds (best kept below 200ms). + ### OTHERS: - Debug: True or False, enable debug mode output on logs. @@ -121,10 +146,19 @@ The higher the value, the more bullish the market needs to be in order to execut - 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. +### TELEGRAM: + +Telegram allows you to remote monitor that status of your running cryptopump instances, and BUY/SELL orders. The currently available command are: + +![](https://github.com/aleibovici/img/blob/b2c9390494906b8e83635a5f320dd48f67a48fbd/telegram_screenshot.jpg?raw=true) + +- /report: Provides Available Funds, Deployed Funds, Profit, Return on Investment, Net Profit, Net Return on Investment, Avg. Transaction Percentage gain, Thread Count, System Status, and Master Node. +- /buy: Buy at the current Master Node thread +- /sell: Sell at the current Master Node thread ## RESUMING AND TROUBLESHOOTING: If you want to stop buy don't want to sell your orders, press stop at each instance. -To resume start the bot, access the first webui, i.e. port 8080, press start. To access the other trading pairs, press new, start the new webui, i.e. port 8081 and press start. Repeat until all instances are resumed. +To resume start the bot, access the first WebUI, i.e. port 8080, press start. To access the other trading pairs, press new, start the new webui, i.e. port 8081 and press start. Repeat until all instances are resumed. If resuming a thread/instance does not work, go into the cryptopump folder and delete the .lock files. Those files are present while the bot is running, if it crashes those won't be deleted so those need to be manually removed before starting the resume process. diff --git a/exchange/exchange.go b/exchange/exchange.go index a0297fe..2d03a42 100644 --- a/exchange/exchange.go +++ b/exchange/exchange.go @@ -609,9 +609,10 @@ S: int64(orderResponse.OrderID)); err != nil { switch { - case strings.Contains(err.Error(), "-2010"), strings.Contains(err.Error(), "-2011"): + 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 */ if orderStatus, err = GetOrder( configData, diff --git a/loader/loader.go b/loader/loader.go index a1fb2f8..d77a199 100644 --- a/loader/loader.go +++ b/loader/loader.go @@ -50,6 +50,7 @@ func LoadSessionDataAdditionalComponents( ProfitThreadID float64 /* ThreadID profit */ ProfitThreadIDPct float64 /* ThreadID profit percentage */ Profit float64 /* Total profit */ + ProfitNet float64 /* Total net profit */ ProfitPct float64 /* Total profit percentage */ ThreadCount int /* Thread count */ ThreadAmount float64 /* Thread cost amount */ @@ -58,6 +59,7 @@ func LoadSessionDataAdditionalComponents( BuyDecisionTreeResult string /* Hold BuyDecisionTree result */ SellDecisionTreeResult string /* Hold SellDecisionTree result */ QuantityOffset float64 /* Quantity offset */ + DiffTotal float64 /* Total difference between target and market price */ Orders []Order } @@ -79,7 +81,7 @@ func LoadSessionDataAdditionalComponents( sessiondata.Session.ThreadID = sessionData.ThreadID sessiondata.Session.SellTransactionCount = sessionData.SellTransactionCount sessiondata.Session.Symbol = sessionData.Symbol[0:3] - sessiondata.Session.SymbolFunds = math.Round((sessionData.SymbolFunds)*100000000) / 100000000 + sessiondata.Session.SymbolFunds = math.Round((sessionData.SymbolFunds)*10000) / 10000 /* Available crypto funds in exchange */ sessiondata.Session.SymbolFiat = sessionData.SymbolFiat sessiondata.Session.SymbolFiatFunds = math.Round(sessionData.SymbolFiatFunds*100) / 100 sessiondata.Session.RateCounter = sessionData.RateCounter.Rate() / 5 /* Average Number of transactions per second proccessed by WsBookTicker */ @@ -88,6 +90,7 @@ func LoadSessionDataAdditionalComponents( sessiondata.Session.QuantityOffset = sessiondata.Session.SymbolFunds /* Quantity offset */ sessiondata.Session.Profit = math.Round(sessionData.Global.Profit*100) / 100 /* Sessions.Global loaded from mySQL via loadSessionDataAdditionalComponentsAsync */ + sessiondata.Session.ProfitNet = math.Round(sessionData.Global.ProfitNet*100) / 100 /* Sessions.Global loaded from mySQL via loadSessionDataAdditionalComponentsAsync */ sessiondata.Session.ProfitPct = math.Round(sessionData.Global.ProfitPct*100) / 100 /* Sessions.Global loaded from mySQL via loadSessionDataAdditionalComponentsAsync */ sessiondata.Session.ProfitThreadID = math.Round(sessionData.Global.ProfitThreadID*100) / 100 /* Sessions.Global loaded from mySQL via loadSessionDataAdditionalComponentsAsync */ sessiondata.Session.ProfitThreadIDPct = math.Round(sessionData.Global.ProfitThreadIDPct*100) / 100 /* Sessions.Global loaded from mySQL via loadSessionDataAdditionalComponentsAsync */ @@ -108,12 +111,37 @@ func LoadSessionDataAdditionalComponents( sessiondata.Session.Orders = append(sessiondata.Session.Orders, tmp) sessiondata.Session.QuantityOffset -= tmp.Quantity /* Quantity offset */ + + sessiondata.Session.DiffTotal += (tmp.Diff * (1 - configData.ExchangeComission)) /* Total difference between target and market price (minus ExchangeComission used for visual aid in UI) */ } + sessiondata.Session.DiffTotal = math.Round(sessiondata.Session.DiffTotal*1) / 1 /* Total difference between target and market price round up for session (local function variable)*/ + sessionData.DiffTotal = sessiondata.Session.DiffTotal /* Total difference between target and market price for session (tranfer value to sessionData struct)*/ + if sessiondata.Session.QuantityOffset >= 0 { /* Only display Quantity offset if negative */ + sessiondata.Session.QuantityOffset = 0 - } else { + sessionData.QuantityOffsetFlag = false + + } else if sessiondata.Session.QuantityOffset < 0 { /* Only display Quantity offset if negative */ + sessiondata.Session.QuantityOffset = math.Round(sessiondata.Session.QuantityOffset*100) / 100 /* Quantity offset */ + + if !sessionData.QuantityOffsetFlag { /* Only log Quantity offset error if first time */ + + logger.LogEntry{ + Config: configData, + Market: nil, + Session: sessionData, + Order: &types.Order{}, + Message: "Quantity offset: " + strconv.FormatFloat(sessiondata.Session.QuantityOffset, 'f', 2, 64), + LogLevel: "DebugLevel", + }.Do() + + } + + sessionData.QuantityOffsetFlag = true /* Quantity offset flag */ + } } @@ -145,9 +173,10 @@ func LoadSessionDataAdditionalComponentsAsync(sessionData *types.Session) { /* Get global data and execute GetProfit if more than 10 seconds since last update. This function is used to prevent multiple threads from running mysql.GetProfit and overloading mySQL server since this is a high cost SQL statement. */ - if profit, profitPct, transactTime, err := mysql.GetGlobal(sessionData); err == nil { + if profit, profitnet, profitPct, transactTime, err := mysql.GetGlobal(sessionData); err == nil { sessionData.Global.Profit = profit /* Load global profit from db */ + sessionData.Global.ProfitNet = profitnet /* Load global net profit from db */ sessionData.Global.ProfitPct = profitPct /* Load global profit from db */ if transactTime == 0 { /* If transactTime is 0 then this is the first time this function is called and insert record into db */ @@ -162,7 +191,7 @@ func LoadSessionDataAdditionalComponentsAsync(sessionData *types.Session) { if time.Since(time.Unix(transactTime, 0)).Seconds() > 10 { /* Only execute GetProfit if more than 10 seconds since last update */ - if sessionData.Global.Profit, sessionData.Global.ProfitPct, err = mysql.GetProfit(sessionData); err != nil { /* Recalculate total profit and total profit percentage */ + if sessionData.Global.Profit, sessionData.Global.ProfitNet, sessionData.Global.ProfitPct, err = mysql.GetProfit(sessionData); err != nil { /* Recalculate total profit and total profit percentage */ return /* Return if error */ diff --git a/main.go b/main.go index 2f4fb1b..16f899f 100644 --- a/main.go +++ b/main.go @@ -97,6 +97,7 @@ func main() { Latency: 0, Status: false, RateCounter: ratecounter.NewRateCounter(5 * time.Second), + DiffTotal: 0, Global: &types.Global{}, } diff --git a/mysql/cryptopump.sql b/mysql/cryptopump.sql index b96fee1..3e327d7 100644 --- a/mysql/cryptopump.sql +++ b/mysql/cryptopump.sql @@ -27,6 +27,7 @@ DROP TABLE IF EXISTS `global`; CREATE TABLE `global` ( `ID` int NOT NULL AUTO_INCREMENT, `Profit` float NOT NULL, + `ProfitNet` float NOT NULL, `ProfitPct` float NOT NULL, `TransactTime` varchar(45) NOT NULL, PRIMARY KEY (`ID`) @@ -72,10 +73,11 @@ CREATE TABLE `session` ( `Exchange` varchar(45) NOT NULL, `FiatSymbol` varchar(45) NOT NULL, `FiatFunds` float NOT NULL, + `DiffTotal` float NOT NULL, `Status` tinyint(1) NOT NULL, PRIMARY KEY (`ID`), UNIQUE KEY `ThreadID_UNIQUE` (`ThreadID`) -) ENGINE=InnoDB AUTO_INCREMENT=1406 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; +) ENGINE=InnoDB AUTO_INCREMENT=1479 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -94,7 +96,7 @@ CREATE TABLE `thread` ( `Price` float NOT NULL, `ExecutedQuantity` float NOT NULL, PRIMARY KEY (`ID`) -) ENGINE=InnoDB AUTO_INCREMENT=8755 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; +) ENGINE=InnoDB AUTO_INCREMENT=8928 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -183,6 +185,7 @@ CREATE DEFINER=`root`@`%` PROCEDURE `GetGlobal`() BEGIN SELECT `global`.`Profit` AS `Profit`, + `global`.`ProfitNet` AS `ProfitNet`, `global`.`ProfitPct` AS `ProfitPct`, `global`.`TransactTime` AS `TransactTime` FROM @@ -403,7 +406,8 @@ DELIMITER ;; CREATE DEFINER=`root`@`%` PROCEDURE `GetProfit`() BEGIN SELECT - SUM(`source`.`Profit`) AS `sum`, + SUM(`source`.`Profit`) AS `profit`, + SUM(`source`.`Profit`) + (`source`.`Diff`) AS `netprofit`, AVG(`source`.`Percentage`) AS `avg` FROM (SELECT @@ -418,7 +422,8 @@ FROM ((`Orders`.`CummulativeQuoteQty` - `orders`.`CummulativeQuoteQty`) / CASE WHEN `Orders`.`CummulativeQuoteQty` = 0 THEN NULL ELSE `Orders`.`CummulativeQuoteQty` - END) AS `Percentage` + 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` @@ -427,7 +432,6 @@ WHERE AND `source`.`Orders__Side` = 'SELL' AND `source`.`Status` = 'FILLED' AND `source`.`Orders__Status` = 'FILLED'); - END ;; DELIMITER ; /*!50003 SET sql_mode = @saved_sql_mode */ ; @@ -449,7 +453,7 @@ BEGIN DECLARE declared_in_param_ThreadID CHAR(50); SET declared_in_param_ThreadID = in_param_ThreadID; SELECT - SUM(`source`.`Profit`) AS `sum`, + SUM(`source`.`Profit`) + (`source`.`Diff`) AS `sum`, AVG(`source`.`Percentage`) AS `avg` FROM (SELECT @@ -464,7 +468,13 @@ FROM ((`Orders`.`CummulativeQuoteQty` - `orders`.`CummulativeQuoteQty`) / CASE WHEN `Orders`.`CummulativeQuoteQty` = 0 THEN NULL ELSE `Orders`.`CummulativeQuoteQty` - END) AS `Percentage` + END) AS `Percentage`, + (SELECT + SUM(`session`.`DiffTotal`) AS `sum` + FROM + `session` + WHERE + `session`.`ThreadID` = declared_in_param_ThreadID) AS `Diff` FROM `orders` INNER JOIN `orders` `Orders` ON `orders`.`OrderID` = `Orders`.`OrderIDSource`) `source` @@ -746,10 +756,10 @@ DELIMITER ; /*!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 `SaveGlobal`(in_Profit float, in_ProfitPct float, in_TransactTime bigint) +CREATE DEFINER=`root`@`%` PROCEDURE `SaveGlobal`(in_Profit float, in_ProfitNet float, in_ProfitPct float, in_TransactTime bigint) BEGIN -INSERT INTO global (Profit, ProfitPct, TransactTime) -VALUES (in_Profit, in_ProfitPct, in_TransactTime); +INSERT INTO global (Profit, ProfitNet, ProfitPct, TransactTime) +VALUES (in_Profit, in_ProfitNet, in_ProfitPct, in_TransactTime); END ;; DELIMITER ; /*!50003 SET sql_mode = @saved_sql_mode */ ; @@ -786,10 +796,10 @@ DELIMITER ; /*!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 `SaveSession`(in_ThreadID varchar(45), in_ThreadIDSession varchar(45), in_Exchange varchar(45), in_FiatSymbol varchar(45), in_FiatFunds float, in_Status tinyint(1)) +CREATE DEFINER=`root`@`%` PROCEDURE `SaveSession`(in_ThreadID varchar(45), in_ThreadIDSession varchar(45), in_Exchange varchar(45), in_FiatSymbol varchar(45), in_FiatFunds float, in_DiffTotal float, in_Status tinyint(1)) BEGIN -INSERT INTO session (ThreadID, ThreadIDSession, Exchange, FiatSymbol, FiatFunds, Status) -VALUES (in_ThreadID, in_ThreadIDSession, in_Exchange, in_FiatSymbol, in_FiatFunds, in_Status); +INSERT INTO session (ThreadID, ThreadIDSession, Exchange, FiatSymbol, FiatFunds, DiffTotal, Status) +VALUES (in_ThreadID, in_ThreadIDSession, in_Exchange, in_FiatSymbol, in_FiatFunds, in_DiffTotal, in_Status); END ;; DELIMITER ; /*!50003 SET sql_mode = @saved_sql_mode */ ; @@ -826,12 +836,13 @@ DELIMITER ; /*!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 `UpdateGlobal`(in_Profit float, in_ProfitPct float, in_TransactTime bigint) +CREATE DEFINER=`root`@`%` PROCEDURE `UpdateGlobal`(in_Profit float, in_ProfitNet float, in_ProfitPct float, in_TransactTime bigint) BEGIN SET SQL_SAFE_UPDATES = 0; UPDATE global SET Profit = in_Profit, + ProfitNet = in_ProfitNet, ProfitPct = in_ProfitPct, TransactTime = in_TransactTime WHERE @@ -879,13 +890,16 @@ DELIMITER ; /*!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 `UpdateSession`(in_ThreadID varchar(45), in_ThreadIDSession varchar(45), in_Exchange varchar(45), in_FiatSymbol varchar(45), in_FiatFunds float, in_Status tinyint(1)) +CREATE DEFINER=`root`@`%` PROCEDURE `UpdateSession`(in_ThreadID varchar(45), in_ThreadIDSession varchar(45), in_Exchange varchar(45), in_FiatSymbol varchar(45), in_FiatFunds float, in_DiffTotal float, in_Status tinyint(1)) BEGIN SET SQL_SAFE_UPDATES = 0; - UPDATE `session` - SET `session`.`FiatFunds` = in_FiatFunds, - `session`.`Status` = in_Status - WHERE `session`.`ThreadID` = in_ThreadID; + UPDATE `session` +SET + `session`.`FiatFunds` = in_FiatFunds, + `session`.`DiffTotal` = in_DiffTotal, + `session`.`Status` = in_Status +WHERE + `session`.`ThreadID` = in_ThreadID; SET SQL_SAFE_UPDATES = 1; END ;; DELIMITER ; @@ -903,4 +917,4 @@ DELIMITER ; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2021-11-23 21:39:25 +-- Dump completed on 2021-12-08 21:09:38 diff --git a/mysql/mysql.go b/mysql/mysql.go index 34fb718..9bb55e9 100644 --- a/mysql/mysql.go +++ b/mysql/mysql.go @@ -245,12 +245,13 @@ func UpdateSession( var rows *sql.Rows - if rows, err = sessionData.Db.Query("call cryptopump.UpdateSession(?,?,?,?,?,?)", + if rows, err = sessionData.Db.Query("call cryptopump.UpdateSession(?,?,?,?,?,?,?)", sessionData.ThreadID, sessionData.ThreadIDSession, configData.ExchangeName, sessionData.SymbolFiat, sessionData.SymbolFiatFunds, + sessionData.DiffTotal, sessionData.Status); err != nil { logger.LogEntry{ @@ -278,8 +279,9 @@ func UpdateGlobal( var rows *sql.Rows - if rows, err = sessionData.Db.Query("call cryptopump.UpdateGlobal(?,?,?)", + if rows, err = sessionData.Db.Query("call cryptopump.UpdateGlobal(?,?,?,?)", sessionData.Global.Profit, + sessionData.Global.ProfitNet, sessionData.Global.ProfitPct, time.Now().Unix()); err != nil { @@ -308,8 +310,9 @@ func SaveGlobal( var rows *sql.Rows - if rows, err = sessionData.Db.Query("call cryptopump.SaveGlobal(?,?,?)", + if rows, err = sessionData.Db.Query("call cryptopump.SaveGlobal(?,?,?,?)", sessionData.Global.Profit, + sessionData.Global.ProfitNet, sessionData.Global.ProfitPct, time.Now().Unix()); err != nil { @@ -339,12 +342,13 @@ func SaveSession( var rows *sql.Rows - if rows, err = sessionData.Db.Query("call cryptopump.SaveSession(?,?,?,?,?,?)", + if rows, err = sessionData.Db.Query("call cryptopump.SaveSession(?,?,?,?,?,?,?)", sessionData.ThreadID, sessionData.ThreadIDSession, configData.ExchangeName, sessionData.SymbolFiat, sessionData.SymbolFiatFunds, + sessionData.DiffTotal, sessionData.Status); err != nil { logger.LogEntry{ @@ -1012,10 +1016,11 @@ func GetProfitByThreadID(sessionData *types.Session) (fiat float64, percentage f // GetProfit retrieve total and average percentage profit func GetProfit( - sessionData *types.Session) (fiat float64, percentage float64, err error) { + sessionData *types.Session) (profit float64, profitNet float64, percentage float64, err error) { var rows *sql.Rows - var fiatNullFloat64 sql.NullFloat64 /* handle null mysql returns */ + var profitNullFloat64 sql.NullFloat64 /* handle null mysql returns */ + var profitNetNullFloat64 sql.NullFloat64 /* handle null mysql returns */ var percentageNullFloat64 sql.NullFloat64 /* handle null mysql returns */ if rows, err = sessionData.Db.Query("call cryptopump.GetProfit()"); err != nil { @@ -1029,17 +1034,46 @@ func GetProfit( LogLevel: "DebugLevel", }.Do() - return fiatNullFloat64.Float64, percentageNullFloat64.Float64, err + return profitNullFloat64.Float64, profitNetNullFloat64.Float64, percentageNullFloat64.Float64, err } for rows.Next() { - err = rows.Scan(&fiatNullFloat64, &percentageNullFloat64) + err = rows.Scan(&profit, &profitNet, &percentage) } rows.Close() - return fiatNullFloat64.Float64, (percentageNullFloat64.Float64 * 100), err + return profit, profitNet, (percentage * 100), err +} + +// GetGlobal get global data +func GetGlobal(sessiondata *types.Session) (profit float64, profitNet float64, profitPct float64, transactTime int64, err error) { + + var rows *sql.Rows + + if rows, err = sessiondata.Db.Query("call cryptopump.GetGlobal()"); err != nil { + + logger.LogEntry{ + Config: nil, + Market: nil, + Session: sessiondata, + Order: &types.Order{}, + Message: functions.GetFunctionName() + " - " + err.Error(), + LogLevel: "DebugLevel", + }.Do() + + return 0, 0, 0, 0, err + } + + for rows.Next() { + err = rows.Scan(&profit, &profitNet, &profitPct, &transactTime) + } + + rows.Close() + + return profit, profitNet, profitPct, transactTime, err + } // GetGlobal get global data diff --git a/telegram/telegram.go b/telegram/telegram.go index 99cb955..411a743 100644 --- a/telegram/telegram.go +++ b/telegram/telegram.go @@ -142,13 +142,13 @@ func CheckUpdates( case "/report": var profit float64 + var profitNet float64 var profitPct float64 - var roi float64 var threadCount int var status string var err error - if profit, profitPct, err = mysql.GetProfit(sessionData); err != nil { + if profit, profitNet, profitPct, err = mysql.GetProfit(sessionData); err != nil { return } @@ -166,14 +166,14 @@ func CheckUpdates( } - /* Calculate total Return on Investment */ - roi = (profit) / (math.Round(sessionData.Global.ThreadAmount*100) / 100) * 100 - Message{ Text: "\f" + "Available Funds: " + sessionData.SymbolFiat + " " + functions.Float64ToStr(sessionData.SymbolFiatFunds, 2) + "\n" + "Deployed Funds: " + sessionData.SymbolFiat + " " + functions.Float64ToStr((math.Round(sessionData.Global.ThreadAmount*100)/100), 2) + "\n" + - "Profit: " + functions.Float64ToStr(profit, 2) + " " + functions.Float64ToStr(profitPct, 2) + "%" + "\n" + - "ROI " + functions.Float64ToStr(roi, 2) + "%" + "\n" + + "Profit: $" + functions.Float64ToStr(profit, 2) + "\n" + + "ROI: " + functions.Float64ToStr(getROI(profit, sessionData), 2) + "%\n" + + "Net Profit: $" + functions.Float64ToStr(profitNet, 2) + "\n" + + "Net ROI: " + functions.Float64ToStr(getROI(profitNet, sessionData), 2) + "%" + "\n" + + "Avg. Transaction: " + functions.Float64ToStr(profitPct, 2) + "%" + "\n" + "Thread Count: " + strconv.Itoa(threadCount) + "\n" + "Status: " + status + "\n" + "Master: " + sessionData.ThreadID, @@ -185,3 +185,11 @@ func CheckUpdates( } } + +// getROI returns the ROI of a given profit +func getROI(profit float64, + sessionData *types.Session) (ROI float64) { + + return (profit) / (math.Round(sessionData.Global.ThreadAmount*100) / 100) * 100 + +} diff --git a/templates/index.html b/templates/index.html index 9827a2f..0228443 100644 --- a/templates/index.html +++ b/templates/index.html @@ -32,8 +32,9 @@
-
-    +
+   +
Threads
@@ -41,10 +42,14 @@
Profit $ - % + $ + %   + Thread Profit $ %   - + Diff + $   +
Deployed $   Funds diff --git a/templates/index_nostart.html b/templates/index_nostart.html index fe20d7c..6b7d774 100644 --- a/templates/index_nostart.html +++ b/templates/index_nostart.html @@ -40,9 +40,11 @@ $('#divIDSessionSymbol_fiat_funds').html(json.Session.SymbolFiatFunds); $('#divIDSessionQuantityOffset').html(json.Session.QuantityOffset); $('#divIDSessionProfit').html(json.Session.Profit); + $('#divIDSessionProfitNet').html(json.Session.ProfitNet); $('#divIDSessionProfitPct').html(json.Session.ProfitPct); $('#divIDSessionProfitThreadID').html(json.Session.ProfitThreadID); $('#divIDSessionProfitThreadIDPct').html(json.Session.ProfitThreadIDPct); + $('#divIDSessionDiffTotal').html(json.Session.DiffTotal); $('#divIDSessionThreadCount').html(json.Session.ThreadCount); $('#divIDSessionThreadAmount').html(json.Session.ThreadAmount); $('#divIDSessionOrders').html(json.Session.Orders); @@ -103,8 +105,9 @@
-
-    +
+   +
Threads
@@ -112,10 +115,14 @@
Profit $ - % + $ + %   + Thread Profit $ %   - + Diff + $   +
Deployed $   Funds diff --git a/types/types.go b/types/types.go index 92f6ebf..514addb 100644 --- a/types/types.go +++ b/types/types.go @@ -104,17 +104,21 @@ type Session struct { RateCounter *ratecounter.RateCounter /* Average Number of transactions per second proccessed by WsBookTicker */ BuyDecisionTreeResult string /* Hold BuyDecisionTree result for web UI */ SellDecisionTreeResult string /* Hold SellDecisionTree result for web UI */ + QuantityOffsetFlag bool /* This flag is true when the quantity is offset */ + DiffTotal float64 /* This variable holds the difference between the total funds and the total funds in the last session */ Global *Global } // Global (Session.Global) struct store semi-persistent values to help offload mySQL queries load type Global struct { Profit float64 /* Total profit */ + ProfitNet float64 /* Net profit */ ProfitPct float64 /* Total profit percentage */ ProfitThreadID float64 /* ThreadID profit */ ProfitThreadIDPct float64 /* ThreadID profit percentage */ ThreadCount int /* Thread count */ ThreadAmount float64 /* Thread cost amount */ + DiffTotal float64 /* /* This variable holds the difference between purchase price and current value across all sessions */ } // Client struct for client libraries