跳转至

Tutorial: ccc_signal_taker

最好将信号生成逻辑和执行逻辑分开,并将信号生成逻辑分解为多个组件。 它允许您重用经过良好测试的组件并避免多次计算相同的值。 Apfiny Algo 的策略框架旨在实现这一目标。

在本教程中,我们将使用变量来实现信号生成逻辑,然后创建策略来使用变量生成的信号。

Create a simple variable

#pragma once
#include "base/Variable.h"

using namespace std;

class TrendVar: public Variable {
public:
    using Variable::Variable;
    void init() override;
    void onQuote(long cid, const SymbolInfo& si) override { setUpdated(); }
    void onTick(long cid, const SymbolInfo& si) override {}
    void onNotify() override;

private:
    SymbolInfo* _si;
    nanoseconds _dur;

    nanoseconds _lastSampleTime = nanoseconds(0);
    double _lastPx = 0.0;
};
#include "TrendVar.h"
#include "components/TimerManager.h"
#include "components/SymbolManager.h"

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

    string ticker = _cfg.at("port").at(0);
    string exch = _cfg.at("port").at(1);
    auto sh = symMgr->getSymbolHandler(ticker, exch);
    sh->addListener(this);
    _si = &sh->symbolInfo();

    int dur = _cfg.at("dur");
    _dur = nanoseconds(dur);
}

void TrendVar::onNotify() {
    const nanoseconds& now = TimerManager::instance()->currentTime();
    if(now > _lastSampleTime + _dur) {
        double px = _si->mid_px();
        if(_lastPx > EPS) {
            _value = px - _lastPx;
            _ready = true;
        }
        else {
            _value = 0.0;
            _ready = false;
        }
        _lastSampleTime = now;
        _lastPx = px;       
    }
}

Create trading strategy

#pragma once
#include "base/Strategy.h"
#include "components/TimerManager.h"
#include "base/Order.h"
#include "base/SymbolInfo.h"
#include "base/Variable.h"

class SignalTakeStrategy: 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:
    void sendIocOrder(TradeDirs side, double px, double qty);

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

    Variable* _signalVar;
    double _order_size;
};
#include "SignalTakeStrategy.h"
#include "components/VariableManager.h"
#include "components/TradeApiManager.h"
#include "components/SymbolManager.h"

void SignalTakeStrategy::init() {
    auto symMgr = SymbolManager::instance();
    auto varMgr = VariableManager::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);

    _order_size = _cfg.at("order_size");

    string signal = _cfg.at("signal");
    _signalVar = varMgr->getVariable(signal);
    addNotifiableChild(_signalVar); 
}

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

    if(!_signalVar->is_ready())
        return;

    if(_hasTimeEvt) {
        _hasTimeEvt = false;

        double midpx = _si->mid_px();
        double signal = _signalVar->value();

        if(signal > 0.01) {
            sendIocOrder(SELL, _si->bid_px, _order_size);
        } else if(signal < -0.01) {
            sendIocOrder(BUY, _si->ask_px, _order_size);
        }
    }
}

void SignalTakeStrategy::sendIocOrder(TradeDirs side, double px, double qty) {
    std::cout << "send IOC order: " << side << " " << px << " " << qty << std::endl;

    LOrder* ord = new LOrder();
    ord->sender = this;
    ord->account = 101;
    ord->use_margin = false;
    ord->margin_source = "spot";
    ord->side = side;
    ord->si = _si;
    ord->px = px;
    ord->qty = qty;
    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 SignalTakeStrategy::onQuote(long cid, const SymbolInfo& si) {

}

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

}

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

Register your variable class and strategy class

class MyComponentFactory : public XlibComponentFactory
{
public:
    Variable *getVariable(const string &type, const string &name, const json &attr) override
    {
        if (type == "TrendVar") 
            return new TrendVar(name, attr);            
        return nullptr;
    }   

    Strategy *getStrategy(const string &type, const string &name, const json &attr) override
    {
        if (type == "SignalTakeStrategy") 
            return new SignalTakeStrategy(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);
    VariableManager::instance()->addXlibComponentFactory(xmgr);
    StrategyManager::instance()->addXlibComponentFactory(xmgr);

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

Configure

我们使用 json 文件来配置交易应用程序该文件包含有关 api 密钥、日志路径和策略组件的信息。 请更新以下示例配置中的 API 密钥信息:

{
    "instance": {
        "license_id":"",
        "license_key":"",
        "log_path": "./signal_taker_test_01",        
        "name": "signal_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": [
        ["BTCUSDTSWAP.OKEX_SWAP_trend30", ["TrendVar", {"port": ["BTCUSDTSWAP", "OKEX_SWAP"], "dur": 1800}]]    
    ],
    "models": [
    ],
    "strategies": [                                 
        ["BTCUSDTSWAP.OKEX_SWAP", [
            "SignalTakeStrategy", {
                "symbol": "BTCUSDTSWAP",
                "trade_market": "OKEX_SWAP",
                "order_size": 0.0001,
                "signal": "BTCUSDTSWAP.OKEX_SWAP_trend30"
            }]]                          
    ]
}

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

Run

Apfiny Algo 依赖于多个共享库,因此您需要先设置一些环境变量。

使用 algo_sdk 的路径设置环境变量 ALGO_HOME。 例如 /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_signal_taker ${ALGO_HOME}/examples/ccc_signal_taker/cfg/signal_taker_cfg.json