Skip to content

Tutorial: ccc_simple_taker

In this tutorial, we will create a more complete trading application. It listens to market data, and sends out an IOC order every minute.

We start using Apfiny Algo strategy framework in the example. The framework uses a plugin mechanism, and trading logic will be implemented as components and registered into the system. After that you use json configuration files to create instances of your strategies and other components.

This example puts all th trading logic into a single component, SimpleTakeStrategy. It inherits from base class Strategy and OrderSender.

Create trading strategy

#include "base/Strategy.h"
#include "components/TimerManager.h"
#include "components/TradeApiManager.h"
#include "components/SymbolManager.h"
#include "base/Order.h"
#include "base/SymbolInfo.h"

class SimpleTakeStrategy: public Strategy, public OrderSender {
public:
    using Strategy::Strategy;

    //implements Strategy
    void init() override;
    void onNotify() override;
    SymbolInfo *getSymbolInfo() override { return _si; }
    void onCommand(json& cmd) override {}
    bool isUseMargin() override { return _useMargin; }
    string& marginSource() override { return _marginSource; }

    //imlements SymbolListener
    void onQuote(long cid, const SymbolInfo& si) override;
    void onTick(long cid, const SymbolInfo& si) override;
    void onQuoteStale(long cid, const SymbolInfo& si) override {}

    //implements TimerListener
    void onTimer(TimerHandler* th, long msecs) override;

    //implements OrderSender
    void onOrderCreated(LOrder* order) override {};
    void onOrderAcked(LOrder* order) override {};
    void onOrderRejected(LOrder* order) override {};
    void onOrderCancelCreated(LOrder* order) override {};
    void onOrderCancelAcked(LOrder* order) override {};
    void onOrderCancelRejected(LOrder* order) override {};
    void onOrderExec(TradeDirs side, double px, double qty, Liquidity liq, LOrder* order) override {};
    void onOrderCanceled(LOrder* order) override {};
    void onOrderClosed(LOrder* order) override {};

protected:
    TimerManager *_timerMgr;
    SymbolInfo* _si;
    bool _useMargin = false;
    string _marginSource = "spot";
    bool _hasTimeEvt = false;

    double _order_size;
    double _take_from_mid;
};
#include "SimpleTakeStrategy.h"

void SimpleTakeStrategy::init() {
    auto symMgr = SymbolManager::instance();

    string symbol = _cfg.at("symbol");
    string exch = _cfg.at("trade_market");
    auto sh = symMgr->getSymbolHandler(symbol, exch);
    sh->addListener(this);
    _si = &sh->symbolInfo();

    _timerMgr = TimerManager::instance();
    TimerHandler* th = _timerMgr->getTimerHandler(60000);
    th->addListener(this);

    _take_from_mid = _cfg.at("take_from_mid_bps").get<double>() * 0.0001;
    _order_size = _cfg.at("order_size");
}

void SimpleTakeStrategy::onNotify() {
    if(!_si->is_ready()) 
        return;

    if(_hasTimeEvt) {
        _hasTimeEvt = false;

        std::cout << "send a random IOC order" << std::endl;

        LOrder* ord = new LOrder();
        ord->sender = this;
        ord->account = 101;
        ord->use_margin = false;
        ord->margin_source = "spot";
        ord->side = BUY;
        ord->si = _si;
        ord->px = _si->mid_px() * (1 + _take_from_mid);
        ord->qty = _order_size;
        ord->remainingQty = ord->qty;
        ord->signal = 0.0;
        ord->spread = _si->spread();
        ord->tif = Tif::TIF_IOC;
        ord->intention = OrderIntention::OI_AGGRESSIVE;

        TradeApi* tradeApi = TradeApiManager::instance()->tradeApi();
        tradeApi->sendOrder(ord);
    }
}

void SimpleTakeStrategy::onQuote(long cid, const SymbolInfo& si) {

}

void SimpleTakeStrategy::onTick(long cid, const SymbolInfo& si) {

}

void SimpleTakeStrategy::onTimer(TimerHandler* th, long msecs) {
    _hasTimeEvt = true;
}

Register your strategy class

class MyComponentFactory : public XlibComponentFactory
{
public:
    Strategy *getStrategy(const string &type, const string &name, const json &attr)
    {
        if (type == "SimpleTakeStrategy") 
            return new SimpleTakeStrategy(name, attr);            
        return nullptr;
    }   
};

Complete the application

int main(int argc, char **argv)
{
    if (argc <= 1)
    {
        std::cout << "Usage: ccc_simple_taker cfg_path" << std::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;

    //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);

    //setup your custom component factory to load components
    XlibComponentFactoryPtr xmgr(new MyComponentFactory);
    StrategyManager::instance()->addXlibComponentFactory(xmgr);

    //start cctrader
    std::cout << "Starting trader ..." << std::endl;
    CCTradeEngine client(cfg);
    client.initialize();
    client.run();
}

Configure

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": {
        "log_path": "./simple_taker_test_01",        
        "name": "simple_taker_test_01"
    },      
    "servers":{
        "redis_server":"127.0.0.1"                           
    }, 
    "exchanges":[
        {"exchange":"OKEX_SWAP","trade_type":"Direct","market_data_type":"Direct"}
    ],
    "apikeys": {
     "OKEX_SWAP": {
        "key": "enter your api key",
        "secret": "enter your api secert",
        "pass": "enter your api passphrase"
         }      
    },
    "fees": {
        "OKEX_SWAP": {
            "make": 0.0,
            "take": 0.0002
        }
    },    
    "symbol_info": {
    }, 
    "symbols": [
        {"cid": 1001, "port": ["BTCUSDTSWAP", "OKEX_SWAP"]}        
    ],
    "samplers": [
    ],
    "pricing_models": [
    ],
    "variables": [      
    ],
    "models": [
    ],
    "strategies": [                                 
        ["BTCUSDTSWAP.OKEX_SWAP", [
            "SimpleTakerStrategy", {
                "symbol": "BTCUSDTSWAP",
                "trade_market": "OKEX_SWAP",
                "take_from_mid_bps": -10.0,
                "order_size": 0.0001
            }]]                          
    ]
}

Build

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_simple_taker/build_scripts
premake4 gmake
make -j 10 config=release

Run

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_simple_taker ${ALGO_HOME}/examples/ccc_simple_taker/cfg/simple_taker_cfg.json