Skip to content

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
Binaries will be generated and copied to algo_sdk/bin/.

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