跳转至

C++ 编程接口

Apifiny Algo 为用户提供了丰富的编程接口。从概念上讲,我们可以将 API 分为 2 组:

  • 高级:量化/交易者可以使用 c++ 来实现算法组件,例如采样器、定价模型、变量和策略。
  • 低级:TradingLib 提供接口让交易者/开发人员使用 c++ 直接访问库的各个方面,例如市场数据回调、下单 api、历史数据播放器等。

组件框架

Algo 的 C++ 代码使用组件框架来管理和扩展功能。所有组件都使用类似的组件管理器和生命周期机制进行管理。

组件生命周期

Algo 中的所有组件都遵循相同的生命周期管理流程:创建、初始化、处理事件回调、处理 onNotify() 回调。

  • Creation:在 Algo 应用程序启动时创建所有组件
  • Initialization:然后初始化所有组件。此时会调用函数 init()。它以一个json对象作为唯一参数,其中包含配置文件中相应组件节点中的属性。 init() 使用 json 对象中的信息来初始化组件,并设置指向 json 对象中指定的其他组件的链接。
  • 处理事件回调:账簿更新、交易更新、计时器更新和采样器回调在发生时进行处理。不同组件的状态可能不同步,有些表示事件之前的状态,有些表示事件之后的状态。因此,此时您应该只处理不依赖于其他组件的数据。
  • 处理 onNotify() 回调:所有组件都处于由最后一个事件触发的更新状态。它们是同步的,你可以在这个函数中实现你的主要逻辑。

注册组件

添加组件类后,需要将其注册到相应的组件管理器中,以便在配置文件中使用。

通过在 XlibComponentFactory 中相应的 getter 函数中添加一行来注册组件。

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;
    }   
};

有两种方法可以打包你的算法组件:作为共享库或在主应用程序代码中。第一个选项允许您将库发布给您的组织或公众; seconds 选项为您提供了一种更紧凑的方式来组织代码。

  • 要将其作为共享库发布,请创建一个名为 MyComponentFactory 的类,该类继承自 XlibComponentFactory,并添加以下行以创建和公开对象 xCompFactory。

        extern "C" BOOST_SYMBOL_EXPORT MyComponentFactory xCompFactory;
        MyComponentFactory xCompFactory;
    

  • 要在主应用程序中包含算法组件,您创建一个名为 MyComponentFactory 的类,该类继承自 XlibComponentFactory,并在主函数中添加以下行以将您的工厂集成到系统中。

        XlibComponentFactoryPtr xmgr(new MyComponentFactory);
        SamplerManager::instance()->addXlibComponentFactory(xmgr);
        PricingModelManager::instance()->addXlibComponentFactory(xmgr);
        VariableManager::instance()->addXlibComponentFactory(xmgr);
        ModelManager::instance()->addXlibComponentFactory(xmgr);
        StrategyManager::instance()->addXlibComponentFactory(xmgr);
    

获取和使用组件

所有组件都由其相应的组件管理器管理,例如变量由VariableManager 管理。组件管理器是单例的。您使用 instance() 获取其指针,然后使用名称字符串调用 getter 函数以获取组件对象。下面是一个关于如何在 json 配置文件中获取名为“trend_ema”的变量的示例。

    VariableManager *varMgr = VariableManager::instance();
    string trend_ema = _cfg.at("trend_ema");
    _trend_ema = varMgr->getVariable(trend_ema);

笔记

出于性能原因,您应该在初始化时获取组件并将它们缓存在您的类中。避免在回调时从组件管理器获取组件。

组件管理器

Component Component Manager
Symbol SymbolManager
Account AccountManager
RiskFormula RiskFormulaManager
Player PlayerManager
Sampler SamplerManager
Variable VariableManager
PricingModel PricingModelManager
Model ModelManager
Strategy StrategyManager

高级 API

量化研究人员和交易者使用此级别的 API 来实现交易算法组件。主要组件是采样器、定价模型、变量、模型和策略。要实现组件,您需要创建一个继承自以下基类之一的类,并覆盖 init()、onNotify() 和其他几个函数。然后您将它注册到您的 XlibComponentFactory 以使其在 json 配置中可用。

Sampler
PricingModel
Variable
Model
Strategy

创建采样器

采样器继承基类 Sampler,实现 init() 函数加载配置,并在事件发生时调用 fireOnSample() 函数。函数 fireOnSample() 将触发对所有侦听此采样器的对象的回调。

下面是一个简单的基于时间的采样器的实现。它继承自 TimerListener,创建一个 TimerHandler,并在 init() 中将自己添加为侦听器。当指定的持续时间过去时,TimerHandler 将调用其侦听器的 onTimer()。我们在此回调中实现 fireOnSampler() 以生成采样器事件。

  • 头文件:TimeSampler.h

    #pragma once
    #include "base/Sampler.h"
    #include "components/TimerManager.h"
    
    class TimeSampler: public Sampler, public TimerListener {
    public:
        using Sampler::Sampler;
        void init();
        void onTimer(TimerHandler* th, long msecs);
    
    private:
        long _msecs;
    };
    

  • 源文件:TimeSampler.cpp

    #include "TimeSampler.h"
    
    void TimeSampler::init() {
        auto timerMgr = TimerManager::instance();
    
        double halflife = _cfg.at("halflife"));
        setHalflife(halflife);
    
        _msecs = _cfg.at("msecs");
        TimerHandler* th = timerMgr->getTimerHandler(msecs);
        th->addListener(this);
    }
    
    void TimeSampler::onTimer(TimerHandler* th, long msecs) {
        if(msecs == _msecs) {
            fireOnSample(1, _decay);
        }
    }
    

  • 注册组件

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

创建定价模型

定价模型计算资产的公允价值并将其存储在其成员变量_price 中。它还有一个布尔变量 _ready 来存储这个公允价值的准备情况。它使用公共方法 price() 和 is_ready() 将这些变量公开。

它继承自基类 PricingModel。 PricingModel 是 SymbolListener 的子类。我们首先实现 init() 函数来加载配置。在大多数情况下,我们重写 onQuote()/onTrade() 来处理市场数据事件,如果我们需要使用的数据在市场数据回调时是脏的,则实现 onNotify()。

下面是一个计算交易品种中间价的示例。它继承自 PricingModel 并监听指定交易品种的市场数据回调。它在 onQuote() 回调中计算中间价,并设置 _price 和 _ready。

!!! note 对于使用此定价模型的对象,我们需要调用 setUpdated() 来触发 onNotify() 回调。

  • 头文件:MidPx.h

    #pragma once
    #include "base/PricingModel.h"
    using namespace std;
    
    class MidPx: public PricingModel {
    public:
        using PricingModel::PricingModel;
        void init();
        void onQuote(long cid, const SymbolInfo& si);
    
    protected:
        SymbolInfo* _si;
    };
    

  • Source file: MidPx.cpp

    #include "MidPx.h"
    #include "components/SymbolManager.h"
    
    void MidPx::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();
    }
    
    void MidPx::onQuote(long cid, const SymbolInfo& si) {
        _price = 0.5 * (si.bid_px + si.ask_px);
        _ready = si.is_ready();
        setUpdated();
    }
    

  • Register the component

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

创建一个变量

Algo 变量保存一个值及其准备情况,并在事件发生时更新它们。对应的C++成员变量是_value和_ready,公共方法是value()和is_ready()。

它继承自基类变量。要创建 Algo 变量,我们首先实现 init() 函数来加载配置。然后我们在回调函数和/或 onNotify() 中进行计算。可能使用脏变量(由于计算树填充过程,其值与当前状态不匹配)的计算应该只发生在 onNotify() 中。

算法变量可用作预测市场走势的特征(有人称之为 alpha),或仅用作帮助进一步计算的支持变量。 Algo 变量的值可以代表任何事物。如果它被设计为一项功能,我们建议您使用它来表示资产回报。

我们在这里以一个相对复杂的 Algo 变量为例。它计算来自多个市场订单簿的加权价格与参考价格(例如 midpx)之间的回报。您可以学习如何获取定价模型并将其用于算法组件,以及如何迭代市场订单簿。

!!!注意 我们需要调用 setUpdated() 来触发使用该变量的对象的 onNotify() 回调。

  • 头文件:BookReturn.h

    #pragma once
    #include "base/Variable.h"
    #include "base/SymbolHandler.h"
    
    class BookReturn: public Variable, public L2ChangeListener {
    public:
        using Variable::Variable;
        void init();
        void onQuote(long cid, const SymbolInfo& si);
        void onL2Change(long cid, const SymbolInfo& si);
        void onNotify();
    
    protected:
        double getBookSize(TradeDirs side);
        double getSizeWeightedPx(TradeDirs side, double sizeTaken);
    
    protected:
        SymbolInfo* _si;
        int _levelCap = 5;
        double _sizeCap = 1000000000;
        bool _bookChanged = true;
        double _weightedPx = 0.0;
        PricingModel* _refPm;
    };
    

  • Source file: BookReturn.cpp

    #include "BookReturn.h"
    #include "components/PricingModelManager.h"
    #include "components/SymbolManager.h"
    
    void BookReturn::init() {
        auto symMgr = SymbolManager::instance();
        auto pmMgr = PricingModelManager::instance();
    
        string ticker = _cfg.at("port").at(0);
        string exch = _cfg.at("port").at(1);
        _sh = symMgr->getSymbolHandler(ticker, exch);
        _sh->addListener(this);
        _sh->addL2ChangeListener(this);
        _si = &_sh->symbolInfo();
    
        _levelCap = getJsonValue(_cfg, "level_cap", 5);
        _sizeCap = getJsonValue(_cfg, "size_cap", 1000000000);
    
        string ref_pm = _cfg.at("ref_pm");
        _refPm = pmMgr->getPricingModel(ref_pm);
        addNotifiableChild(_refPm);
    }
    
    void BookReturn::onL2Change(long cid, const SymbolInfo& si) {
        _bookChanged = true;
    }
    
    void BookReturn::onQuote(long cid, const SymbolInfo& si) {
        _bookChanged = true;
        setUpdated();
    }
    
    void BookReturn::onNotify() {
        if(_bookChanged) {
            double bidSizeSum = getBookSize(BUY);
            double askSizeSum = getBookSize(SELL);
            double sizeTaken = min(min(bidSizeSum, askSizeSum), _sizeCap);
    
            double weightedBidPx = getSizeWeightedPx(BUY, sizeTaken);
            double weightedAskPx = getSizeWeightedPx(SELL, sizeTaken);
            _weightedPx = 0.5 * (weightedBidPx + weightedAskPx);
            _bookChanged = false;
        }
    
        double refpx = _refPm->price();
        _value = (refpx > EPS) ? _weightedPx / refpx - 1.0 : 0.0;
        _ready = _si->is_ready() && _refPm->is_ready();
    }
    
    double BookReturn::getBookSize(TradeDirs side) {
        LevelAccessor& la = _si->getBookLevelAccessor(side);
        Level* level = la.begin();
        int i = 0;
        double csz = 0;
        while(level) {
            if(i >= _levelCap)
                break;
            csz += level->qty;
            level = la.next();
            ++i;
        }
        return csz;
    }
    
    double BookReturn::getSizeWeightedPx(TradeDirs side, double sizeTaken) {
        LevelAccessor& la = _si->getBookLevelAccessor(side);
        Level* level = la.begin();
        int i = 0;
        double szpx = 0;
        double csz = 0;
        while(level) {
            if(i >= _levelCap)
                break;
            double sz = level->qty;
            csz += sz;
            if(csz >= sizeTaken) {
                sz -= (csz - sizeTaken);
                csz = sizeTaken;
                szpx += sz * level->price;
                break;
            }
            szpx += sz * level->price;
            level = la.next();
            ++i;
        }
        return szpx / csz;
    }
    

  • Register the component

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

创建模型

我们使用模型将信号输入到交易策略中。它们可以是变量的简单传递,也可以包含高级逻辑,例如在不同条件下切换到不同的变量。

  • 头文件:SimpleModel.h

    #pragma once
    #include "base/Model.h"
    #include "base/Variable.h"
    using namespace std;
    
    class SimpleModel: public Model{
    public:
        using Model::Model;
        void init();
        void onNotify();
    
    private:
        Variable* _var;
    };
    

  • Source file: SimpleModel.cpp

    #include "SimpleModel.h"
    #include "components/VariableManager.h"
    
    void SimpleModel::init()
    {
        auto varMgr = VariableManager::instance();
        _var = varMgr->getVariable(_cfg.at("variable"));
        addNotifiableChild(_var);
    }
    
    void SimpleModel::onNotify() {
        _signal = _var->value();
        _ready = _var->is_ready();
    }
    

  • Register the component

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

创建策略

算法策略旨在实现订单执行逻辑。它从模型和变量中获取信号输入,然后相应地放置或取消订单。它处理订单更新回调并管理头寸/风险。它是交易策略实施的核心代码。

Algo 策略继承自基类 Strategy。我们还包括一个参考实现,以解决创建策略时的常见任务。我们将给出一个直接继承自 Strategy 的示例和另一个继承自 CCModelStrategy 的示例。

从策略继承
  • 头文件:SignalTakeStrategy.h

    #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;
    };
    

  • Source file: SignalTakeStrategy.cpp

    #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 the component

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

Inherit from CCModelStrategy

Algo SDK 包括一些从 Strategy 继承的策略类。这些类之间的关系是:

classDiagram
    Strategy <|-- BaseStrategy
    BaseStrategy <|-- CCBaseStrategy
    CCBaseStrategy <|-- CCModelStrategy

我们将在下面的例子中继承CCModelStrategy:

  • 头文件:CCTakerStrategy.h

    #pragma once
    #include "strategies/CCModelStrategy.h"
    
    class CCTakerStrategy: public CCModelStrategy {
    public:
        using CCModelStrategy::CCModelStrategy;
        void init() override;
    
    protected:
        void tradeOnSignal(double signal) override;
    
    protected:
        double _max_spread;
    };
    

  • Source file: CCTakerStrategy.cpp

    #include "CCTakerStrategy.h"
    
    void CCTakerStrategy::init() {
        CCModelStrategy::init();
    
        _max_spread = getJsonValue(_cfg, "max_spread_bps", 10000.0) * 0.0001;
    
        std::cout << "created CCTakerStrategy model:" << _model->name() << std::endl;
    }
    
    void CCTakerStrategy::tradeOnSignal(double signal){
        const nanoseconds& eventTime = _timerMgr->currentTime();
    
        double depPx = _depPm->price();
        double pos = _account->position(_si->cid);
        double bbo_from_mid = 0.5 * _si->spread() / _si->mid_px();
    
        double ret_from_mid = returnFromMid(signal, _depPm);
    
        TradeDirs side = (ret_from_mid > 0) ? BUY : SELL;
        bool expPos = willExpandPosition(side, pos);
        double thresh = expPos ? _take_r_thold : _low_take_r_thold;
    
        if(fabs(ret_from_mid) < thresh + bbo_from_mid)
            return;
    
        if(_si->spread() > _max_spread * _si->mid_px())
            return;
    
        TradeDirs oppoSide = oppositeSide(side);
        double notional = _account->notional(_si->cid);
        double oppoPx = _si->getOppositeBestPrice(side);
        double tgt_px = _si->mid_px() * (1.0 + ret_from_mid + ((side==BUY) ? -thresh : thresh));
    
        double sweep_px = oppoPx * (1.0 + ((side==BUY) ? _max_sweep : -_max_sweep));
        double desired_px = (side==BUY) ? min(sweep_px, tgt_px) : max(sweep_px, tgt_px);
    
        double px = roundPrice(desired_px, side);
        double ioc_size = _si->isUsdFutures() ? int(_ioc_notional / _si->contract_multiplier) : _ioc_notional / _si->contract_multiplier / px;
        double qty = expPos ? ioc_size : min(ioc_size, fabs(pos));
    
        LOrder* ord = sendIocOrder(side, px, qty, signal);
        if(ord) {
            if(_isLive)
                cout << toStr(TimerManager::instance()->currentTime())
                    << " cid:" << _si->cid
                    << " side:" << side << " px:" << px << " qty:" << qty
                    << " bid:" << _si->bid_px << " ask:" << _si->ask_px
                    << " tgt_px:" << tgt_px
                    << " signal: " << signal << " ret_from_mid: " << ret_from_mid << endl;
        }
    }
    

  • Register the component

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

底层 API

您可以使用低级 API 访问系统中的许多功能。

SymbolInfo 和 SymbolHandler

SymbolInfo 包含交易品种的静态特征,例如分时大小、手数、最小订单大小、价格精度、数量精度、合约倍数等。它还提供交易品种的最新信息,例如中间价、买价/询价、数量等。SymbolHandler 首先初始化交易品种的静态信息,并在 onQuote/onTick/onTrade 触发时保持更新交易品种的实时信息。

以下是您可以从 SymbolInfo 访问的信息:

#pragma once
#include "common/common.h"
#include "base/Consts.h"
#include "base/IOrderBookManager.h"

class SymbolInfo {
public:
    bool is_ready() const { return _ready; }
    bool is_stale() const { return _stale; }
    double mid_px() const { return _mid_px; }
    double spread() const { return _spread; }
    double risk_px() const { return _risk_px; }
    //double open_vwap() const { return _open_vwap; }
    double open_mid_avg() const { return _open_mid_avg; }

    double mid_xbbo() const { return 0.5 * (bid_xbbo + ask_xbbo); }

    double ticks2Price(int ticks) const { return ticks * tick_size; }
    int getTicks(double px) const { return int((px + EPS) / tick_size); }
    double getXbboPrice(TradeDirs side) const { return (side == BUY) ? bid_xbbo : ask_xbbo; }
    double getTbboPrice(TradeDirs side) const { return (side == BUY) ? bid_tbbo : ask_tbbo; }
    double getBBOPrice(TradeDirs side) const { return (side == BUY) ? bid_px : ask_px; }
    double getBBOQty(TradeDirs side) const { return (side == BUY) ? bid_qty : ask_qty; }
    double getBestPrice(TradeDirs side) const { return (side == BUY) ? bid_px : ask_px; }
    double getOppositeBestPrice(TradeDirs side) const { return (side == SELL) ? bid_px : ask_px; }
    double improvedTicks(TradeDirs side, double tpx, double px) const {
        double ticks = (tpx - px) / tick_size;
        return (side==BUY) ? ticks : -ticks;
    }
    double getImprovedPrice(double improvedTicks, TradeDirs side) const {
        double improvedPx = improvedTicks * tick_size;
        return (side==BUY) ? bid_px + improvedPx: ask_px - improvedPx;
    }

    void setPrevClosePrice(double px) { _prevClosePx = px; }
    double prevClosePrice() { return _prevClosePx; }

    void setCalendarDaysSincePrevTradingDay(int days) { _calendarDaysSincePrevTradingDay = days; }
    int calendarDaysSincePrevTradingDay() { return _calendarDaysSincePrevTradingDay; }

    void setOrderBookManager(IOrderBookManager* orderBookMgr) { _orderBookMgr = orderBookMgr; }
    IOrderBookManager* getOrderBookManager() { return _orderBookMgr; }
    LevelAccessor& getBookLevelAccessor(TradeDirs side) { return _orderBookMgr->getLevelAccessor(side); }

    double calcBookQty(TradeDirs side, double deepestPrice, bool inclusive=true);

    double getLevelPrice(TradeDirs side, int level, bool isPrevBook=false) const;
    double getLevelQty(TradeDirs side, int level, bool isPrevBook=false) const;
    int getLevelOrds(TradeDirs side, int level, bool isPrevBook=false) const;
    double getQtyOnLevels(TradeDirs side, double deepestPrice, bool inclusive=true, bool isPrevBook=false) const;
    double getPrevQtyOnLevels(TradeDirs side, double deepestPrice, bool inclusive=true) const;

    string getProductName() const { return SymbolUtils::getProductName(ticker, exchange); }
    string getLocalSymbolName() const { return SymbolUtils::getLocalSymbolName(ticker, exchange); }
    const string& getExchangeFullName() const { return SymbolUtils::getExchangeFullName(exchange); }
    string converToTradeVenue() const {return SymbolUtils::converToTradeVenue(exchange); }
    string converToCVenue() const{ return SymbolUtils::converToCVenue(exchange);}
    string  converToTradeSymbol() const{ return SymbolUtils::converToTradeSymbol(ticker);}
    // string converToCVenue const {return SymbolUtils::converToCVenue(exchange);}


    void setConfig(json& cfg) { _cfg = cfg; }
    json& getConfig() { return _cfg; }

    bool isUsdFutures() const { return asset_class == AssetClass::CC_USD_FUTURES; }
    bool isUsdtFutures() const { return asset_class == AssetClass::FUTURES; }
    bool isCryptoSpot() const { return asset_class == AssetClass::CC; }
    double calcNotional(double qty) const { return qty * contract_multiplier * (isUsdFutures() ? 1.0 : _risk_px); }
    double calcNotional(double qty, double px) const  { return qty * contract_multiplier * (isUsdFutures() ? 1.0 : px); }
    double qtyFromNotional(double notional) const { return notional / contract_multiplier / (isUsdFutures() ? 1.0 : _risk_px); }
    double qtyFromNotional(double notional, double px) const  { return notional / contract_multiplier / (isUsdFutures() ? 1.0 : px); }

    bool validate_trade(double price, double qty) const;

    double getCoinBalance(AccounType type);
    double getBaseCoinBalance(AccounType type);
    double getLongContractPosition();
    double getShortContractPosition();
    double getNetContractPosition();


public:
    json _cfg;
    uint64_t cid = -1;
    string ticker;
    string exchange;
    string local_name;

    //options
    string underlying;
    string expiry;
    double strike;
    bool is_call;

    bool active = true;
    bool trade = false;
    double tick_size = 0.01;
    double lot_size = 1;
    double min_order_size = 1.0;
    double contract_multiplier = 1;
    int qty_precision = 0;
    int price_precision = 2;
    int lever = 1;
    AssetClass asset_class;

    double _prevClosePx = std::numeric_limits<double>::quiet_NaN();
    int _calendarDaysSincePrevTradingDay = 1;

    double trade_px = 0;
    double trade_qty = 0;
    int trade_type = 0;
    double volume = 0;
    double bid_px = 0;
    double ask_px = 0;
    double bid_qty = 0;
    double ask_qty = 0;
    int64_t bid_ords = 0;
    int64_t ask_ords = 0;
    nanoseconds quote_time = nanoseconds(0);
    nanoseconds trade_time = nanoseconds(0);

    double bid_tbbo = 0;
    double ask_tbbo = 0;

    double bid_xbbo = 0;
    double ask_xbbo = 0;
    double bid_xbbo_qty = 0;
    double ask_xbbo_qty = 0;

    //double _open_vwap = 0.0;
    double _open_mid_avg = 0.0;
    double _mid_px = 0.0;
    double _spread = 0.0;
    double _risk_px = 0.0;
    bool _hasRiskPrice = false;
    bool _stale = false;
    bool _ready = false;

    bool _useIBdata = false; //ib qty is in lot !!!

    IOrderBookManager* _orderBookMgr = nullptr;
};

处理市场数据回调

市场数据回调有三个主要类别,onTick、onQuote 和 onL2Change。 onTick 将在市场发生交易时触发。一旦 BBO 级别发生变化,就会触发 onQuote。只要市场深度发生变化,就会触发 onL2Change。

您可以使用以下回调处理这些事件:

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

下单和取消订单

您可以使用tradeAPI下单或取消订单,下面是发送订单的示例函数:

`` LOrder* BaseStrategy::sendOrderImpl(TradeDirs side, double px, double qty, double signal, Tif tif, OrderIntention intention, bool checkRisk) { double addedRisk = _riskMultiple * _si->calcNotional(qty) * ((side==BUY) ? 1.0 : -1.0); double futureRisk = _risk + addedRisk; double absRisk = fabs(_risk); double absFutureRisk = fabs(futureRisk); if(checkRisk && (absFutureRisk > _max_risk) && (absFutureRisk > absRisk)) { //SLOG << " orderBlocked(exceeding_risk) risk=" << _risk << " futureRisk=" << futureRisk << endl; return nullptr; }

double pos = _account->position(_si->cid);
double notional = _account->notional(_si->cid);
bool expanding_pos = willExpandPosition(side);
if(expanding_pos) {
    if(fabs(notional) > _max_notional) {
        //SLOG << " orderBlocked(exceeding_max_notional) pos=" << pos << " notional=" << notional << endl;
        return nullptr;
    }
    if((px < _min_px) || (px > _max_px)) {
        //SLOG << " orderBlocked(price out of range) px=" << px << endl;
        return nullptr;
    }
}

if(miscle(_si->spread(), 0)) {
    //SLOG << " orderBlocked(invalid spread) spread=" << _si->spread() << endl;
    return nullptr;
}

const nanoseconds& now = _timerMgr->currentTime();

LOrder* ord = new LOrder();
ord->sender = this;
ord->account = _account->accountId();
ord->use_margin = _use_margin;
ord->margin_source = _margin_source;
ord->side = side;
ord->si = _si;
ord->px = px;
ord->qty = qty;
ord->remainingQty = qty;
ord->signal = signal;
ord->spread = _si->spread();
ord->tif = tif;
ord->intention = intention;
ord->noticeId = _notMgr->getNoticeId();
ord->is_post_only = true;

double bbopx = _si->getBBOPrice(side);
if(priceBetterThan(px, bbopx, side))
    ord->estQtyAhead = 0;
else if(misceq(px, bbopx))
    ord->estQtyAhead = _si->getBBOQty(side);
else
    ord->estQtyAhead = 1e6;

LOrder *order = _tradeApi->sendOrder(ord);
if(side == BUY) _last_buy_order = now;
else if(side == SELL) _last_sell_order = now;
return order;

} ```

处理订单更新回调

onOrder* 将在订单状态从交易所发生变化时触发,其中 * 表示相应的状态。 您可以使用以下订单回调来管理您的订单生命周期。 ``` 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;
};

}; ```

Get Balance

To get the balance of a given trading pair, there are two methods.

Using BalanceManager

We first define the following: BalanceManager* _balanceMgr; BalanceItem* _coinBalanceItem = NULL; BalanceItem* _baseBalanceItem = NULL; We then fetch the coins in the trading pair from SymbolInfo, and generate the two BalanceItems, which have the balance property. string coin = _si->coin; string baseCoin = _si->base_coin; string exchName = _si->exchange; string accttype = "spot"; // Allowed values: "spot". "isolated", "cross" string coinItemName = coin + "-" + exchName + "-" + accttype; string baseItemName = baseCoin + "-" + exchName + "-" + accttype; _balanceMgr = BalanceManager::instance(); _coinBalanceItem = _balanceMgr->getBalanceItem(coinItemName); _baseBalanceItem = _balanceMgr->getBalanceItem(baseItemName);

Using SymbolInfo

SymbolInfo also has a function that you can call to fetch your balances of its associated trading pair. double getCoinBalance(AccounType type); double getBaseCoinBalance(AccounType type); AccounType is an enum that can take on the values ACCOUNT_TYPE_SPOT, ACCOUNT_TYPE_CROSS, and ACCOUNT_TYPE_ISOLATED.

Get Contract Position

SymbolInfo has methods to get your contract position in its trading pair: double getLongContractPosition(); double getShortContractPosition(); double getNetContractPosition();