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