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