Skip to content
This repository has been archived by the owner on Mar 12, 2023. It is now read-only.

Commit

Permalink
* Added t flag to specify to use the testnet config
Browse files Browse the repository at this point in the history
* Added stop loss based on given percentage by strategy (#10)
* Fixed some retCodes that gave issues
  • Loading branch information
Arne Wouters committed Aug 25, 2020
1 parent b154485 commit aa9d91b
Show file tree
Hide file tree
Showing 7 changed files with 57 additions and 21 deletions.
36 changes: 23 additions & 13 deletions bytra/source/Bybit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ Bybit::Bybit(std::string &baseUrl, std::string &apiKey, std::string &apiSecret,
getCandlesApi();

position = std::make_shared<Position>();
position->stopLossPercentage = strategy->getStopLossPercentage();
orderBook = std::make_shared<OrderBook>();
}

Expand Down Expand Up @@ -129,12 +130,12 @@ void Bybit::getPositionApi() {
throw std::runtime_error("Bad API response.");
}

std::string side = (std::string)response["result"]["side"];
double entryPrice = std::stod((std::string) response["result"]["entry_price"]);
std::string side = (std::string) response["result"]["side"];
long qty = (long)response["result"]["size"];
if (side == "Sell") {
qty = -qty;
}
position->qty = qty;
if (side == "Sell") { qty = -qty; }

position->update(qty, entryPrice);
}

void Bybit::cancelAllActiveOrders() {
Expand Down Expand Up @@ -256,7 +257,7 @@ void Bybit::readWebsocket() {
}

void Bybit::parseWebsocketMsg(const std::string &msg) {
// std::cout << msg << std::endl;
std::cout << msg << std::endl;

dom::parser parser;
dom::element response = parser.parse(msg);
Expand Down Expand Up @@ -284,12 +285,12 @@ void Bybit::parseWebsocketMsg(const std::string &msg) {

if (topic == "position") {
for (dom::object item : response["data"]) {
double entryPrice = std::stod((std::string) item["entry_price"]);
std::string side = (std::string)item["side"];
long qty = (long)item["size"];
if (side == "Sell") {
qty = -qty;
}
position->qty = qty;
if (side == "Sell") { qty = -qty; }

position->update(qty, entryPrice);
}

} else if (topic == "order") {
Expand Down Expand Up @@ -466,7 +467,7 @@ void Bybit::placeMarketOrder(const Order &ord) {

int retCode = response["ret_code"].get_int64();

if (retCode != 0) {
if (retCode != 0 && retCode != 30063) {
spdlog::error("Bybit::placeMarketOrder - bad response - " + (std::string)response["ret_msg"]);
throw std::runtime_error("Bad API response.");
}
Expand Down Expand Up @@ -552,12 +553,12 @@ void Bybit::amendLimitOrder(const Order &ord) {

int retCode = response["ret_code"].get_int64();

if (retCode != 0 && retCode != 30032 && retCode != 30037) {
if (retCode != 0 && retCode != 30032 && retCode != 30037 && retCode != 20001) {
spdlog::error("Bybit::amendLimitOrder - bad response - " + (std::string)response["ret_msg"]);
throw std::runtime_error("Bad API response.");
}

if (retCode == 30032 || retCode == 30037) { position->activeOrder = nullptr; }
if (retCode == 30032 || retCode == 30037 || retCode == 20001) { position->activeOrder = nullptr; }
}

void Bybit::cancelActiveLimitOrder() {
Expand Down Expand Up @@ -678,6 +679,15 @@ void Bybit::doAutomatedTrading() {
}
}
}

// Stop Loss
if (position->qty != 0) {
double midPrice = (orderBook->askPrice() + orderBook->bidPrice()) / 2;
if ((position->isLong() && midPrice < position->stopLossPrice) || (position->isShort() && midPrice > position->stopLossPrice)) {
if (position->activeOrder) { cancelActiveLimitOrder(); }
placeMarketOrder(Order(-position->qty, true));
}
}
}

void Bybit::removeUnusedCandles() {
Expand Down
12 changes: 12 additions & 0 deletions bytra/source/Position.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,25 @@
class Position {
public:
long qty = 0;
double entryPrice = 0;
double stopLossPercentage = 0.03;
double stopLossPrice = 0;
std::shared_ptr<Order> activeOrder;

Position() = default;

[[nodiscard]] bool isShort() const { return qty < 0; }

[[nodiscard]] bool isLong() const { return qty > 0; }

void update(const long &newQty, const double &newPrice) {
qty = newQty;
entryPrice = newPrice;

if (qty == 0) { return; }
else if (isLong()) { stopLossPrice = entryPrice - entryPrice*stopLossPercentage; }
else {stopLossPrice = entryPrice + entryPrice*stopLossPercentage;}
}
};

#endif // MEXTRA_POSITION_H
17 changes: 9 additions & 8 deletions bytra/source/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@
#include <spdlog/spdlog.h>
#include <unistd.h>

#include <boost/beast/core/buffers_to_string.hpp>
#include <boost/beast/core/flat_buffer.hpp>
#include <boost/beast/websocket/ssl.hpp>
#include <chrono>
#include <cli/CLI11.hpp>
#include <csignal>
Expand Down Expand Up @@ -49,6 +46,8 @@ int main(int argc, char **argv) {
app.add_option("-s,--strategy,strategy", strategy, "Name of the strategy")->required();
int d{0};
app.add_flag("-d,--debug", d, "Debug flag that enables debug logging");
int t{0};
app.add_flag("-t,--testnet", t, "Testnet flag that makes it use the testnet configuration");

CLI11_PARSE(app, argc, argv)

Expand Down Expand Up @@ -100,11 +99,13 @@ int main(int argc, char **argv) {
std::cout << strategy << " strategy found! " << GREEN << "" << RESET << std::endl;
std::cout << " - Setting up strategy" << std::flush;

std::string baseUrl = *tbl["bybit-testnet"]["baseUrl"].value<std::string>();
std::string websocketHost = *tbl["bybit-testnet"]["websocketHost"].value<std::string>();
std::string websocketTarget = *tbl["bybit-testnet"]["websocketTarget"].value<std::string>();
std::string apiKey = *tbl["bybit-testnet"]["apiKey"].value<std::string>();
std::string apiSecret = *tbl["bybit-testnet"]["apiSecret"].value<std::string>();
std::string configEntry = t ? "bybit-testnet" : "bybit";

std::string baseUrl = *tbl[configEntry]["baseUrl"].value<std::string>();
std::string websocketHost = *tbl[configEntry]["websocketHost"].value<std::string>();
std::string websocketTarget = *tbl[configEntry]["websocketTarget"].value<std::string>();
std::string apiKey = *tbl[configEntry]["apiKey"].value<std::string>();
std::string apiSecret = *tbl[configEntry]["apiSecret"].value<std::string>();

auto bybit = std::make_shared<Bybit>(baseUrl, apiKey, apiSecret, websocketHost, websocketTarget,
validStrategies[strategy]);
Expand Down
1 change: 1 addition & 0 deletions bytra/source/strategies/Ema.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Ema::Ema() {
qty = 100;
orderType = "Market";
slippage = 10.0;
stopLossPercentage = 0.03;
}

bool Ema::checkLongEntry(std::map<TimeFrame, std::vector<std::shared_ptr<Candle>>> &candles) {
Expand Down
1 change: 1 addition & 0 deletions bytra/source/strategies/Rsi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ Rsi::Rsi() {
qty = 100;
orderType = "Limit";
slippage = 5.0;
stopLossPercentage = 0.03;
}

bool Rsi::checkLongEntry(std::map<TimeFrame, std::vector<std::shared_ptr<Candle>>> &candles) {
Expand Down
3 changes: 3 additions & 0 deletions bytra/source/strategies/Strategy.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class Strategy {
std::string symbol;
std::string orderType;
double slippage;
double stopLossPercentage = 0.03;

public:
virtual bool checkLongEntry(std::map<TimeFrame, std::vector<std::shared_ptr<Candle>>> &candles) = 0;
Expand All @@ -43,6 +44,8 @@ class Strategy {
[[nodiscard]] long getQty() const { return qty; }

[[nodiscard]] double getSlippage() const { return slippage; }

[[nodiscard]] double getStopLossPercentage() const { return stopLossPercentage; }
};

#endif // MEXTRA_STRATEGY_H
8 changes: 8 additions & 0 deletions data/configuration.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,11 @@ websocketHost = "stream-testnet.bybit.com"
websocketTarget = "/realtime"
apiKey = "insert_api_key"
apiSecret = "insert_api_secret"

[bybit]
name = "Bybit"
baseUrl = "https://api.bybit.com"
websocketHost = "stream.bybit.com"
websocketTarget = "/realtime"
apiKey = "insert_api_key"
apiSecret = "insert_api_secret"

0 comments on commit aa9d91b

Please sign in to comment.