Tutorial: ccc_executor
In this tutorial, let's create a simple application that places orders and handles order update callbacks.
Implement a simple strategy
The SDK uses base class OrderSender to hold order update callback functions. We first inherit this class and implement the callbacks:
class MyStrategy : public OrderSender
{
public:
using OrderSender::OrderSender;
// implements OrderSender
void onOrderCreated(LOrder *order) override
{
cout << "order created. local id = " << order->orderId << endl;
};
void onOrderAcked(LOrder *order) override
{
cout << "order ack. local id = " << order->orderId << ", id = " << order->remoteOrderId << endl;
};
void onOrderRejected(LOrder *order) override
{
cout << "order rejected. local id = " << order->orderId << endl;
};
void onOrderCancelCreated(LOrder *order) override
{
cout << "order cancel created. id = " << order->remoteOrderId << endl;
};
void onOrderCancelAcked(LOrder *order) override
{
cout << "order cancel ack. id = " << order->remoteOrderId << endl;
};
void onOrderCancelRejected(LOrder *order) override
{
cout << "order cancel rejected. id = " << order->remoteOrderId << endl;
};
void onOrderExec(TradeDirs side, double px, double qty, Liquidity liq, LOrder *order) override
{
cout << "order exec. id = " << order->remoteOrderId << (side == BUY ? " buy" : "sell")
<< "order px: " << px << " size: " << qty << endl;
};
void onOrderCanceled(LOrder *order) override
{
cout << "order canceled. id = " << order->remoteOrderId << endl;
};
void onOrderClosed(LOrder *order) override
{
cout << "order closed. id = " << order->remoteOrderId << endl;
};
};
Now let's write some code to load configuration files and setup loggers.
int main(int argc, char **argv)
{
if (argc <= 1)
{
cout << "Usage: ccc_executor cfg_path" << endl;
return 0;
}
// load configuration
std::time_t t = std::time(0);
std::tm *now = std::localtime(&t);
int date = (now->tm_year + 1900) * 10000 + (now->tm_mon + 1) * 100 + now->tm_mday;
json cfg = loadConfig(argv[1], date);
json &instCfg = cfg["instance"];
instCfg["tradeDate"] = date;
instCfg["isLive"] = true;
instCfg["listenAllSymbol"] = true;
// setup logger
string instName = getJsonValue(instCfg, "name", string("instname"));
string logPath = getJsonValue(instCfg, "log_path", string("."));
date = instCfg["tradeDate"];
logPath += "/" + to_string(date);
mkdirs(logPath);
logPath += "/inst_" + instName + ".log";
int logLevel = getJsonValue(instCfg, "log_level", qts::log4z::LOG_LEVEL_DEBUG);
QTS_LOG_START(logLevel, logPath);
ConfigManager::instance()->setJsonCfg(cfg);
We then get the TraderApiManager instance and SymbolManager instance. These are singletons. You place orders through TraderApiManager, and use SymbolManager to manage symbols. We create one symbol here so we can trade it later.
auto tradeApiMgr = TradeApiManager::instance();
auto symMgr = SymbolManager::instance();
string symbol = "BTCUSDT";
string exch = "BINANCEUS";
auto sh = symMgr->getSymbolHandler(symbol, exch);
auto si = &sh->symbolInfo();
After adding symbols, it is time to start the main loop of Apfiny Algo framework:
CCTradeEngine client(cfg);
client.initialize();
client.async_run();
Now we can create an instance of our MyStrategy class, and implement some dummy trading logic. In this application, we send out an IOC order every 60 seconds.
To send an order out, we first create an LOrder object, and then use tradeApi to send. We need set the sender attribute in order to receive order update callback.
MyStrategy *strat = new MyStrategy();
TradeApi *tradeApi = tradeApiMgr->tradeApi(); // Must be called after initialize() function
sleep(10);
while (true)
{
cout << "send a random IOC order" << endl;
LOrder *ord = new LOrder();
ord->sender = strat;
ord->account = 101;
ord->use_margin = true;
ord->margin_source = "cross";
ord->side = BUY;
ord->si = si;
ord->px = 39300.0;
ord->qty = 0.001;
ord->remainingQty = ord->qty;
ord->signal = 0.0;
ord->spread = si->spread();
ord->tif = Tif::TIF_IOC;
ord->intention = OrderIntention::OI_AGGRESSIVE;
tradeApi->sendOrder(ord);
sleep(60);
}
}
Now we have a complete trading application, and it is time to build, configure and run.
Build the application
The release contains the complete source code and the premake file used to build it. Follow the steps below to build:
cd algo_sdk/examples/ccc_executor/build_scripts
premake4 gmake
make -j 10 config=release
Create a configuration file
We use a JSON file to configure the trading applications. The file contains information about api keys, log path, and strategy components. Please update the API key information in the example configuration below:
{
"instance": {
"license_id":"",
"license_key":"",
"log_path": "./ccc_executor_test_01",
"name": "ccc_executor_test_01"
},
"servers":{
"redis_server":"127.0.0.1"
},
"exchanges":[
{"exchange":"BINANCE_SWAP","trade_type":"Direct","market_data_type":"None"}
],
"apikeys": {
"BINANCE_SWAP": {
"key": "enter your api key",
"secret": "enter your api secert",
"pass": "enter your api passphrase"
}
},
"fees": {
"BINANCE_SWAP": {
"make": 0.0,
"take": 0.0002
}
},
"symbol_info": {
},
"symbols": [
],
"samplers": [
],
"pricing_models": [
],
"variables": [
],
"models": [
],
"strategies": [
]
}
Run the application
Apfiny Algo depends on several shared libraries, so you need setup some environment variables first.
Set environment variable ALGO_HOME using the path of your algo_sdk. e.g. /data/cc/algo_sdk:
export ALGO_HOME=YOUR_ALGO_SDK_PATH
Setup other environment variables:
export TZ=UTC
export LD_LIBRARY_PATH=${ALGO_HOME}/bin:$LD_LIBRARY_PATH
export PATH=${ALGO_HOME}/bin:$PATH
You can start the application now. It takes one command line argument, which is the path to a json configuration file.
ccc_executor ${ALGO_HOME}/examples/ccc_executor/cfg/executor_cfg.json