头图

Preface

It seems that a long time has passed since the author published a platform article last time, and it is a bit embarrassing. On the one hand, the author’s daily work is relatively busy. Even if the platform functions have been improving, there is not much time to organize and publish the articles. On the other hand, the series of articles on the platform architecture published before basically cover the main points of platform design, and we need to go deeper. In terms of technical details, it will inevitably be boring.
This time the platform released a large version v0.7.0 , the content of the changes involved more points. Simply looking at the update log, it will be more difficult for a while to figure out what changes have been made in this major version. So I took this opportunity to organize an article to introduce the new features of this major version, and at the same time brush a wave of existence, I hope you don’t forget the WonderTrader
Some people may ask that many versions have been updated before, and I haven't seen you send an article to introduce it so formally, so what is special about this version of v0.7.0
v0.7.0 is indeed a very special version. In addition to the daily routine bug repairs, performance optimizations, and function adjustments, the v0.7.0 is the addition of some new components, which are very important basic components on which the WonderTrader
WonderTrader has always adhered to two design principles during the architecture design stage: 16142b343d353f focuses on low coupling in technology and focus on . Low coupling is easier to understand. WonderTrader are connected through interfaces, which reduces coupling, reduces the difficulty of component development, and improves the efficiency of team collaborative development. So what does manageability mean? The previous platform articles actually revealed one thing more or less: that is, WonderTrader is a platform designed for quantitative teams. In addition to covering individual needs, it also fully considers the management needs of quantitative teams.
Before the v0.7.0 version, WonderTrader mainly focused on the transaction management, which is the management of the real trading environment, while the v0.7.0 the management function to the team and the investment research stage. The management needs are now, and v0.7.0 is also focused on investment and research management.
In addition, v0.7.0 has another change, which is to the webui source code under 16142b343d35c5 wtpy and put it separately into the open source project wtconsole . Here are the WonderTrader , wtpy and wtconsole introduce the new features of v0.7.0

WonderTrader new features

WonderTrader as C++ , carries more tasks that provide basic functions and operational efficiency. v0.7.0 version, WonderTrader also added several basic components, and made some optimizations according to the needs.

Local data servo module

The local data servo module WtDtServo is one of the two most important new features in the v0.7.0 WtDtPorter previously provided data component export module 06142b343d36a5, WtDtPorter provides services such as market access, data landing, and market broadcast. It is a service module developed for the data needs of real transactions. The modules involved include the data landing module WtDataWriter And data reading module WtDataReader .
Local data servo module WtDtServo , the nature and WtDataReader function is the same, both from WtDataWriter reading data arrival data file, but WtDtServo as independent data servo module, to the application layer is more convenient access to a WonderTrader landed data interface. WtDataReader is to provide data access interface for real-time transaction framework. Therefore WtDataReader position marker during operation of real-time quotes based on the time stamp, synchronization data read and WtDtServo is to provide a non-marker may random data access interface needed .
WtDtServo main application scenario of for data acquisition in the 16142b343d373b investment and research link. Because the data requirements for investment and research are unrelated, there is no inevitable logical sequence between the data pulled each time. Based on this application scenario, WtDtServo mainly provides 4 interfaces:

  • Obtain K-line data according to the time interval get_bars_by_range
  • Obtain K-line data according to the number and cut-off time get_bars_by_count
  • Obtain tick data according to the time interval get_ticks_by_range
  • Obtain tick data according to the number and deadline get_ticks_by_count

The following code shows the definition of each interface:

#pragma once
#include "PorterDefs.h"

#ifdef __cplusplus
extern "C"
{
#endif

    EXPORT_FLAG void        initialize(WtString cfgFile, bool isFile);

    EXPORT_FLAG    WtString    get_version();

    EXPORT_FLAG    WtUInt32    get_bars_by_range(const char* stdCode, const char* period, WtUInt64 beginTime, WtUInt64 endTime, FuncGetBarsCallback cb);

    EXPORT_FLAG    WtUInt32    get_ticks_by_range(const char* stdCode, WtUInt64 beginTime, WtUInt64 endTime, FuncGetTicksCallback cb);

    EXPORT_FLAG    WtUInt32    get_bars_by_count(const char* stdCode, const char* period, WtUInt32 count, WtUInt64 endTime, FuncGetBarsCallback cb);

    EXPORT_FLAG    WtUInt32    get_ticks_by_count(const char* stdCode, WtUInt32 count, WtUInt64 endTime, FuncGetTicksCallback cb);

#ifdef __cplusplus
}
#endif  

Pub/Sub message queue module

The message queue module WtMsgQue is the other one of the two most important new features of v0.7.0 The message queue module is the most basic component WonderTrader With the message queue module, many components can be decoupled more reasonably, so as not to affect the operating efficiency, but also to better achieve the management goals.
Considering that WonderTrader real disk framework and the backtest framework are not complicated, the message queue module nanomsg , and only the pub/sub protocol is used. That is, the real disk framework or the backtest framework will publish the end, and realize a receiving end at the application layer, so as to achieve the purpose of message push.
In order to facilitate the use, WtMsgQue made a very thorough encapsulation, provides a C interface, the application layer can call this module separately. The definition of the module interface is as follows:

#pragma once
#include "PorterDefs.h"

#ifdef __cplusplus
extern "C"
{
#endif
    EXPORT_FLAG void        regiter_callbacks(FuncLogCallback cbLog);

    EXPORT_FLAG WtUInt32    create_server(const char* url, bool confirm);
    EXPORT_FLAG void        destroy_server(WtUInt32 id);
    EXPORT_FLAG void        publish_message(WtUInt32 id, const char* topic, const char* data, WtUInt32 dataLen);

    EXPORT_FLAG WtUInt32    create_client(const char* url, FuncMQCallback cb);
    EXPORT_FLAG void        destroy_client(WtUInt32 id);
    EXPORT_FLAG void        subscribe_topic(WtUInt32 id, const char* topic);
    EXPORT_FLAG void        start_client(WtUInt32 id);
#ifdef __cplusplus
}
#endif

Minimum impact execution unit

WonderTrader provides a built-in execution unit factory module WtExecFact , which provides a simplest execution unit WtSimpleExecUnit . The execution unit is named Simple because the order logic is very simple: you can select the best price, the latest price or the counterparty price as the base price through parameters, and then control the base price offset through a price offset parameter Hops to get the commission price, and then the limit order out one time. If exceeds the preset time and the order is not fully cancel the original order , and then re-execute the pending order logic. This is repeated until the actual position matches the target position.
The simple execution unit is basically competent when the number of orders is small. According to the statistics of the real market, the probability of order cancellation is probably within 5% , which is still relatively small. However, once the number of transactions increases, too many orders are placed at a time, which will cause a great market impact. If the active order (the counterparty price pending order) is used, it will easily penetrate the counterparty price and promote the price to move in an unfavorable direction. If passive order (optimized pending order) is used, a larger pending order will be formed at the optimal price, and will be reversed by other market participants.
In order to meet the needs of a larger number of transactions, reduce market shocks, and reduce transaction costs, algorithmic trading is available. The more popular TWAP and VWAP . In this update, a simplified TWAP execution unit has also been added, called the minimum impact execution unit WtMinImpactExecUnit . The main logic of the minimal impact execution unit is basically the same. The execution unit is basically the same. It is still based on the base price ± offset hops. If the order expires, the order will be cancelled and the order The main difference is that the impact of the minimum execution unit each single quantity of may be provided fixed number , or with a pending order rival disc a certain percentage of a number of computing . In addition, since each order is placed in a small quantity, when the number of transactions is large, will be divided into multiple orders . At this time, set the time interval between two orders to control the frequency of .
The following code is the core calculation logic of the minimal impact execution unit:

void WtMinImpactExeUnit::do_calc()
{
    if (_cancel_cnt != 0)
        return;

    //这里加一个锁,主要原因是实盘过程中发现
    //在修改目标仓位的时候,会触发一次do_calc
    //而ontick也会触发一次do_calc,两次调用是从两个线程分别触发的,所以会出现同时触发的情况
    //如果不加锁,就会引起问题
    //这种情况在原来的SimpleExecUnit没有出现,因为SimpleExecUnit只在set_position的时候触发
    StdUniqueLock lock(_mtx_calc);

    double newVol = get_real_target(_target_pos);
    const char* stdCode = _code.c_str();

    double undone = _ctx->getUndoneQty(stdCode);
    double realPos = _ctx->getPosition(stdCode);
    double diffPos = newVol - realPos;

    //有未完成订单,与实际仓位变动方向相反
    //则需要撤销现有订单
    if (decimal::lt(diffPos * undone, 0))
    {
        bool isBuy = decimal::gt(undone, 0);
        OrderIDs ids = _ctx->cancel(stdCode, isBuy);
        if(!ids.empty())
        {
            _orders_mon.push_order(ids.data(), ids.size(), _ctx->getCurTime());
            _cancel_cnt += ids.size();
            _ctx->writeLog("[%s@%d] live opposite order of %s canceled, cancelcnt -> %u", __FILE__, __LINE__, _code.c_str(), _cancel_cnt);
        }
        return;
    }

    //因为是逐笔发单,所以如果有不需要撤销的未完成单,则暂不发单
    if (!decimal::eq(undone, 0))
        return;

    double curPos = realPos;

    //检查下单时间间隔
    uint64_t now = TimeUtils::getLocalTimeNow();
    if (now - _last_place_time < _entrust_span)
        return;

    if (_last_tick == NULL)
        _last_tick = _ctx->grabLastTick(stdCode);

    if (_last_tick == NULL)
    {
        _ctx->writeLog("No lastest tick data of %s, execute later", _code.c_str());
        return;
    }

    if (decimal::eq(curPos, newVol))
    {
        //当前仓位和最新仓位匹配时,如果不是全部清仓的需求,则直接退出计算了
        if (!is_clear(_target_pos))
            return;

        //如果是清仓的需求,还要再进行对比
        //如果多头为0,说明已经全部清理掉了,则直接退出
        double lPos = _ctx->getPosition(stdCode, 1);
        if (decimal::eq(lPos, 0))
            return;

        //如果还有都头仓位,则将目标仓位设置为非0,强制触发
        newVol = -min(lPos, _order_lots);
        _ctx->writeLog("Clearing process triggered, target position of %s has been set to %f", _code.c_str(), newVol);
    }

    bool bForceClose = is_clear(_target_pos);

    bool isBuy = decimal::gt(newVol, curPos);

    //如果相比上次没有更新的tick进来,则先不下单,防止开盘前集中下单导致通道被封
    uint64_t curTickTime = (uint64_t)_last_tick->actiondate() * 1000000000 + _last_tick->actiontime();
    if (curTickTime <= _last_tick_time)
    {
        _ctx->writeLog("No tick of %s updated, execute later", _code.c_str());
        return;
    }

    _last_tick_time = curTickTime;

    double this_qty = _order_lots;
    if (_by_rate)
    {
        this_qty = isBuy ? _last_tick->askqty(0) : _last_tick->bidqty(0);
        this_qty = round(this_qty*_qty_rate);
        if (decimal::lt(this_qty, 1))
            this_qty = 1;

        this_qty = min(this_qty, abs(newVol - curPos));
    }

    double buyPx, sellPx;
    if(_price_mode == -1)
    {
        buyPx = _last_tick->bidprice(0) + _comm_info->getPriceTick() * _price_offset;
        sellPx = _last_tick->askprice(0) - _comm_info->getPriceTick() * _price_offset;
    }
    else if(_price_mode == 0)
    {
        buyPx = _last_tick->price() + _comm_info->getPriceTick() * _price_offset;
        sellPx = _last_tick->price() - _comm_info->getPriceTick() * _price_offset;
    }
    else if(_price_mode == 1)
    {
        buyPx = _last_tick->askprice(0) + _comm_info->getPriceTick() * _price_offset;
        sellPx = _last_tick->bidprice(0) - _comm_info->getPriceTick() * _price_offset;
    }
    else if(_price_mode == 2)
    {
        double mp = (_last_tick->bidqty(0) - _last_tick->askqty(0))*1.0 / (_last_tick->bidqty(0) + _last_tick->askqty(0));
        bool isUp = (mp > 0);
        if(isUp)
        {
            buyPx = _last_tick->askprice(0) + _comm_info->getPriceTick() * _cancel_times;
            sellPx = _last_tick->askprice(0) - _comm_info->getPriceTick() * _cancel_times;
        }
        else
        {
            buyPx = _last_tick->bidprice(0) + _comm_info->getPriceTick() * _cancel_times;
            sellPx = _last_tick->bidprice(0) - _comm_info->getPriceTick() * _cancel_times;
        }
    }

    //检查涨跌停价
    bool isCanCancel = true;
    if (!decimal::eq(_last_tick->upperlimit(), 0) && decimal::gt(buyPx, _last_tick->upperlimit()))
    {
        _ctx->writeLog("Buy price %f of %s modified to upper limit price", buyPx, _code.c_str(), _last_tick->upperlimit());
        buyPx = _last_tick->upperlimit();
        isCanCancel = false;    //如果价格被修正为涨跌停价,订单不可撤销
    }
    
    if (!decimal::eq(_last_tick->lowerlimit(), 0) && decimal::lt(sellPx, _last_tick->lowerlimit()))
    {
        _ctx->writeLog("Sell price %f of %s modified to lower limit price", buyPx, _code.c_str(), _last_tick->upperlimit());
        sellPx = _last_tick->lowerlimit();
        isCanCancel = false;    //如果价格被修正为涨跌停价,订单不可撤销
    }

    if (isBuy)
    {
        OrderIDs ids = _ctx->buy(stdCode, buyPx, this_qty, bForceClose);
        _orders_mon.push_order(ids.data(), ids.size(), _ctx->getCurTime(), isCanCancel);
    }
    else
    {
        OrderIDs ids  = _ctx->sell(stdCode, sellPx, this_qty, bForceClose);
        _orders_mon.push_order(ids.data(), ids.size(), _ctx->getCurTime(), isCanCancel);
    }

    _last_place_time = now;
}

Process adjustment and performance optimization

In addition to the relatively large new features mentioned earlier, v0.7.0 has also made many small modifications, mainly focusing on some process adjustments and performance optimization.

  • automatically clean up historical main contract positions after the main contract month is changed. After the main contract changes the month, the net position of the main contract of the previous period will be cleared, and a position will be established on the new main contract. However, because 16142b343d3c9c net positions, if the long and short positions are equal, the net position is also 0. In the old version, WonderTrader will not automatically clear the remaining positions, requiring manual intervention. v0.7.0 optimizes this area. After the net position is 0, it will continue to judge whether there is a unilateral position. If there is a unilateral position, continue to close the position until there is no long or short position, the real position It is also up to 0.
  • The stock has realized the support for the post-restoration data. already supports the processing of stock restoration data at the beginning of the open source, but the old version was relatively simple at that time, and the restoration processing was not restored backtest, so the backtested transaction prices are all Restoration price. not only adds support for post-restoration data, but also restores the restoration data during the performance of the backtest, in addition to not considering the benefits of dividends, has been It's almost the same as the real deal.
  • The performance of the data acquisition interface has been optimized. WtBtPorter and WtPorter are glue modules that interact between the entire bottom layer and the application layer, and all C interfaces interact. At the application layer acquiring historical market data, the old version using one by one data callback way of passing data to the application layer, and the new version is used block callback way to transfer data. Here the greatest advantage is to reduce the number of callback function, which can improve the efficiency of data read about% 10 .
//老版本获取K线的接口
WtUInt32 cta_get_bars(CtxHandler cHandle, const char* stdCode, const char* period, WtUInt32 barCnt, bool isMain, FuncGetBarsCallback cb)
{
    CtaMocker* ctx = getRunner().cta_mocker();
    if (ctx == NULL)
        return 0;
    try
    {
        WTSKlineSlice* kData = ctx->stra_get_bars(stdCode, period, barCnt, isMain);
        if (kData)
        {
            WtUInt32 left = barCnt + 1;
            WtUInt32 reaCnt = 0;
            WtUInt32 kcnt = kData->size();
            for (uint32_t idx = 0; idx < kcnt && left > 0; idx++, left--)
            {
                WTSBarStruct* curBar = kData->at(idx);
                //每一根bar都要进行回调传回给应用层
                bool isLast = (idx == kcnt - 1) || (left == 1);
                cb(cHandle, stdCode, period, curBar, isLast);
                reaCnt += 1;
            }

            kData->release();
            return reaCnt;
        }
        else
        {
            return 0;
        }
    }
    catch(...)
    {
        return 0;
    }
}
//v0.7.0版本获取K线的接口
WtUInt32 cta_get_bars(CtxHandler cHandle, const char* stdCode, const char* period, WtUInt32 barCnt, bool isMain, FuncGetBarsCallback cb)
{
    CtaMocker* ctx = getRunner().cta_mocker();
    if (ctx == NULL)
        return 0;
    try
    {
        WTSKlineSlice* kData = ctx->stra_get_bars(stdCode, period, barCnt, isMain);
        if (kData)
        {
            uint32_t left = barCnt;
            uint32_t reaCnt = min(barCnt, (uint32_t)kData->size());
            //将历史数据块和实时数据块分两次回调传回给应用层
            if (kData->get_his_count() > 0)
            {
                uint32_t thisCnt = min(left, (uint32_t)kData->get_his_count());
                left -= thisCnt;
                reaCnt += thisCnt;
                cb(cHandle, stdCode, period, kData->get_his_addr(), thisCnt, left == 0);
            }

            if (left > 0 && kData->get_rt_count() > 0)
            {
                uint32_t thisCnt = min(left, (uint32_t)kData->get_rt_count());
                left -= thisCnt;
                reaCnt += thisCnt;
                cb(cHandle, stdCode, period, kData->get_rt_addr(), thisCnt, true);
            }

            kData->release();
            return reaCnt;
        }
        else
        {
            return 0;
        }
    }
    catch(...)
    {
        return 0;
    }
}

New features of wtpy

wtpy addition to the python WonderTrader , it also provides a lot of easy-to-use practical tools. wtpy In addition to the adaptation of the underlying modules, the most important functions are the monitoring service component WtMonSvr and the performance analysis component WtBtAnalyst . These two components do not depend on the bottom layer, and are functional components of the pure application layer. v0.7.0 version, the monitoring service component WtMonSvr has also made major adjustments.

Bottom docking optimization

The aforementioned WonderTrader underlying Porter interfaces, the data callback from a single block of data into the callback callback , so wtpy in wrapper also do the appropriate optimization. wrapper utilized ctypes the addressof and fromaddress two methods, resolve directly underlying memory address returned , thereby improving the conversion efficiency, improve performance.
At the same time, the global variable global modifier is removed, and all local variables are used to improve the execution efficiency in python

def on_stra_get_bar(self, id:int, stdCode:str, period:str, curBar:POINTER(WTSBarStruct), count:int, isLast:bool):
    '''
    获取K线回调,该回调函数因为是python主动发起的,需要同步执行
    @id     策略id
    @stdCode   合约代码
    @period K线周期
    @curBar 最新一条K线
    @isLast 是否是最后一条
    '''
    engine = self._engine
    ctx = engine.get_context(id)
    period = bytes.decode(period)

    bsSize = sizeof(WTSBarStruct)
    addr = addressof(curBar.contents) # 获取内存地址
    bars = [None]*count # 预先分配list的长度
    for i in range(count):
        realBar = WTSBarStruct.from_address(addr)   # 从内存中直接解析成WTSBarStruct
        bar = dict()
        if period[0] == 'd':
            bar["time"] = realBar.date
        else:
            bar["time"] = 1990*100000000 + realBar.time
        bar["bartime"] = bar["time"]
        bar["open"] = realBar.open
        bar["high"] = realBar.high
        bar["low"] = realBar.low
        bar["close"] = realBar.close
        bar["volume"] = realBar.vol
        bars[i] = bar
        addr += bsSize

    if ctx is not None:
        ctx.on_getbars(bytes.decode(stdCode), period, bars, isLast)
    return

Low-level message queue module application

C++ bottom layer of pub/sub message queue implementation module WtMsgQue nanomsg . This module is packaged with C interface and can be used independently. Therefore, wtpy also implements a python version of the WtMsgQue module, which directly calls the underlying interface.

class WtMQServer:

    def __init__(self):
        self.id = None

    def init(self, wrapper:WtMQWrapper, id:int):
        self.id = id
        self.wrapper = wrapper

    def publish_message(self, topic:str, message:str):
        if self.id is None:
            raise Exception("MQServer not initialzied")

        self.wrapper.publish_message(self.id, topic, message)

class WtMQClient:

    def __init__(self):
        return

    def init(self, wrapper:WtMQWrapper, id:int):
        self.id = id
        self.wrapper = wrapper

    def start(self):
        if self.id is None:
            raise Exception("MQClient not initialzied")

        self.wrapper.start_client(self.id)

    def subscribe(self, topic:str):
        if self.id is None:
            raise Exception("MQClient not initialzied")
        self.wrapper.subcribe_topic(self.id, topic)

    def on_mq_message(self, topic:str, message:str, dataLen:int):
        pass

@singleton
class WtMsgQue:

    def __init__(self) -> None:
        self._servers = dict()
        self._clients = dict()
        self._wrapper = WtMQWrapper(self)

        self._cb_msg = CB_ON_MSG(self.on_mq_message)

    def get_client(self, client_id:int) -> WtMQClient:
        if client_id not in self._clients:
            return None
        
        return self._clients[client_id]

    def on_mq_message(self, client_id:int, topic:str, message:str, dataLen:int):
        client = self.get_client(client_id)
        if client is None:
            return

        client.on_mq_message(topic, message, dataLen)

    def add_mq_server(self, url:str, server:WtMQServer = None) -> WtMQServer:
        id = self._wrapper.create_server(url)
        if server is None:
            server = WtMQServer()

        server.init(self._wrapper, id)
        self._servers[id] = server
        return server

    def destroy_mq_server(self, server:WtMQServer):
        id = server.id
        if id not in self._servers:
            return
        
        self._wrapper.destroy_server(id)
        self._servers.pop(id)

    def add_mq_client(self, url:str, client:WtMQClient = None) -> WtMQClient:
        id = self._wrapper.create_client(url, self._cb_msg)
        if client is None:
            client = WtMQClient()
        client.init(self._wrapper, id)
        self._clients[id] = client
        return client

    def destroy_mq_client(self, client:WtMQClient):
        id = client.id
        if id not in self._clients:
            return
        
        self._wrapper.destroy_client(id)
        self._clients.pop(id)
        

Complete monitoring service components

v0.7.0 version, the monitoring service component WtMonSvr , mainly improved the following types of interfaces:

  • User management interface, such as password modification, permission control, role assignment, etc.
  • Combination management interface, such as viewing signal filtering rules, setting signal filtering rules, etc.
  • Configure the management interface, such as viewing configuration files, modifying configuration files, etc.

Task scheduling component optimization

This is mainly the optimization of the task scheduling component WatchDog
The old version starts subprocess in a separate thread, and monitors whether the child process is stopped in real time, redirects the child process's console, and pushes the log output from the console to the monitoring page. The disadvantage of this method is that the monitoring service cannot be restarted, because is restarted, the already started process cannot be remounted. In a production environment, all processes transactions are not just restart . However, if the old process cannot be remounted, it means that any push information of the strategy combination to be monitored cannot be received.
In view of the above shortcomings, v0.7.0 completely abandoned the original process scheduling method. Still use subprocess start the process, but use the psutil library to check the status of the process. In this way, if you encounter a restart of the monitoring service, you only need to use psutil obtain the list of existing processes and compare the command line to achieve the purpose of remounting. At the same time based on WtMsgQue achieve a EventReceiver , for receiving child process by WtMsgQue push messages posted component, and pushed to the monitoring page.

Backtest management component implementation

WtBtMon management component 06142b343d413e is also a very important v0.7.0 Mainly provides the following services:

  • Online strategy management. The establishment of different management directories for different users, users can wtconsole released webui remote policy management operations such as add, delete, modify.
  • Online strategy backtesting. It mainly provides the scheduling and management of back-test tasks. It is essentially the realization of a task scheduling component, but it is aimed at back-test tasks.
  • Backtest result management. It mainly includes various transaction data, performance analysis data, and historical K-line data generated by backtesting. Wherein K line data history, is based on WtDtServo data read servo assembly, then through WtMonSvr an interface to the webui service.

Main contract tool

For the convenience of users, v0.7.0 also shares a practical tool, the main contract tool WtHotPicker . This tool can pull historical market data from the exchange, or from WonderTrader closing operation, so as to read the largest contract as the main contract and the second largest contract as the Sub-main contract, and then generate a main contract switching rule hots.json and sub-main contract switching rule seconds.json .
If a notification email is configured, the main contract tool will also notify the recipient of the daily main contract and sub-main contract switching status via email.
主力合约切换通知

{
    "CFFEX": {
        "IC": [
            {
                "date": 20210715,
                "from": "IC2107",
                "newclose": 6862.4,
                "oldclose": 6909.2,
                "to": "IC2108"
            },
            {
                "date": 20210819,
                "from": "IC2108",
                "newclose": 6874.4,
                "oldclose": 6916.6,
                "to": "IC2109"
            }
        ],
        "IF": [
            {
                "date": 20210715,
                "from": "IF2107",
                "newclose": 5052.4,
                "oldclose": 5075.0,
                "to": "IF2108"
            },
            {
                "date": 20210819,
                "from": "IF2108",
                "newclose": 4850.8,
                "oldclose": 4879.0,
                "to": "IF2109"
            }
        ],
        "IH": [
            {
                "date": 20210715,
                "from": "IH2107",
                "newclose": 3328.6,
                "oldclose": 3337.4,
                "to": "IH2108"
            },
            {
                "date": 20210819,
                "from": "IH2108",
                "newclose": 3173.4,
                "oldclose": 3184.4,
                "to": "IH2109"
            }
        ]
    }
}

New features of wtconsole

wtconsole has just been wtpy as a separate warehouse. Because it is webui , there are many details of the interaction, so this article will not list them one by one, but mainly shows the interface of the new functions of this update.

Portfolio Management Module

组合管理之组合持仓
组合管理之组合风控
组合管理之组合绩效

Portfolio configuration management

配置管理

View notification list

通知列表

Online backtest

策略回测管理
回测绩效概览
回测信号分析
回测成交明细
回测信号明细
回测回合明细
回测每日绩效

Concluding remarks

In this paper, WonderTrader the release of v0.7.0 the new features of version introduced to this end. In fact, there are many changes in the details of the new version, so this article will not list them one by one. Especially when it comes to the webui interaction, there are more details. More functions, please find out in the use of interested readers. I hope that readers can have a more comprehensive understanding of this major version through this article, so that they can get started more quickly when using it in the future.
v0.7.0 version is released, it is mainly small function iteration for the time being. The author is very happy that there is a great god who wants to wtpy . The next stage is to adapt it first to help the great god get it right as soon as possible. The second is to improve the online backtest part, so that the author can use it to do promotion. After all, with a usable interactive interface, there is no need to talk about it, and you can do some analysis in conjunction with the chart. The third is to prepare to adapt to more varieties, including options, funds, cryptocurrency and so on. Finally, I want to improve the document a bit, after all, the document has not been updated for a long time.
WonderTrader aims to provide better wheels for all quantitative practitioners, encapsulate technology-related things in the platform, and strive to bring better strategy development experience to strategy research and development. The author's level is limited, and I hope that more people will pay attention to this project and participate in this project, so as to make the WonderTrader more complete and more efficient.

Finally, Amway WonderTrader

WonderTrader 's github address: https://github.com/wondertrader/wondertrader

WonderTrader official document address: https://wondertrader.github.io

wtpy 's github address: https://github.com/wondertrader/wtpy

06142b343d45b1 of github : https://github.com/wondertrader/wtconsole


market is risky and investment needs to be cautious. The above statement only serves as a review of historical events, does not represent views on the future, and does not serve as any investment advice.


WonderTrader
31 声望23 粉丝