Compiling CTP in Go

CTP, developed by Shanghai Futures Exchange Technology, is a core futures trading system renowned for its speed and stability. It provides high-performance trade execution, real-time risk management, and market data services, making it the preferred underlying technology platform for professional investors and futures companies.

Go is an open-source programming language developed by Google, known for its simple syntax, efficient concurrency, and fast compilation. It combines the safety of statically typed languages with the flexibility of dynamic languages, making it ideal for building high-performance, scalable network services and cloud-native applications.

Since CTP is written in C++, it cannot be called directly from Go. A viable technical approach is to use SWIG to translate and wrap the CTP C++ code, and then call it using cgo. This article details this method.

Assume the working directory is /home/netxianren/workspace/myctp/src/myctp, containing the following files:

  • error.dtd
  • error.xml
  • libthostmduserapi_se.so
  • libthosttraderapi_se.so
  • ThostFtdcMdApi.h
  • ThostFtdcTraderApi.h
  • ThostFtdcUserApiDataType.h
  • ThostFtdcUserApiStruct.h
  • go.mod
  • libmyctp.go
  • myctp.swigcxx

The first eight files are provided by Shanghai Futures Exchange Technology. The two dynamic link library files, libthostmduserapi_se.so and libthosttraderapi_se.so, are renamed versions of the official files thostmduserapi_se.so and thosttraderapi_se.so, with “lib” prepended to the filenames.

The go.mod file specifies the Go module name and version.

module myctp

go 1.21

The libmyctp.go file contains cgo directives to link the C++ libraries.

//go:build (linux && cgo) || (windows && cgo)
// +build linux,cgo windows,cgo

// Copyright 2012 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package myctp

/*
#cgo linux LDFLAGS: -fPIC -L${SRCDIR}/ -lthostmduserapi_se -lthosttraderapi_se -lstdc++
#cgo linux CPPFLAGS: -fPIC -I${SRCDIR}/
*/
import "C"

The myctp.swigcxx file contains the SWIG interface definition, including directives for handling C++ virtual functions in Go and typemaps for converting between C++ and Go types.

/* Copyright 2011 The Go Authors.  All rights reserved.
   Use of this source code is governed by a BSD-style
   license that can be found in the LICENSE file.  */

/* An example of writing a C++ virtual function in Go.  */

%module(directors="1") myctp

%header %{
#include "ThostFtdcUserApiDataType.h"
#include "ThostFtdcUserApiStruct.h"
#include "ThostFtdcMdApi.h"
#include "ThostFtdcTraderApi.h"
%}

%init %{ 
  //printf("Initialization qerio.goctp done.\n");
%}



%typemap(gotype) (char **ppInstrumentID, int nCount) "[]string"


%typemap(in) (char **ppInstrumentID, int nCount)
%{
  {
    int i;
    _gostring_* a;

    $2 = $input.len;
    a = (_gostring_*) $input.array;
    $1 = (char **) malloc (($2 + 1) * sizeof (char *));
    for (i = 0; i < $2; i++) {
      
      /* Not work */
      //_gostring_ *ps = &a[i];
      //$1[i] = (char *) ps->p;
      //$1[i][ps->n] = '\0';

      /*Work well*/
      _gostring_ *ps = &a[i];
      $1[i] = (char*) malloc(ps->n + 1);
      memcpy($1[i], ps->p, ps->n);
      $1[i][ps->n] = '\0';

    }
    $1[i] = NULL;
  }
%}

%typemap(argout) (char **ppInstrumentID, int nCount) "" /* override char *[] default */

%typemap(freearg) (char **ppInstrumentID, int nCount)
%{
  {
    int i;
    for (i = 0; i < $2; i++)
    {
      free ($1[i]);
    }
    free($1);
  }
%}



/* Parse the header files to generate wrappers */
%include "std_string.i"

%feature("director") CThostFtdcMdSpi;
%feature("director") CThostFtdcTraderSpi;

%include "ThostFtdcUserApiDataType.h"
%include "ThostFtdcUserApiStruct.h"
%include "ThostFtdcMdApi.h"
%include "ThostFtdcTraderApi.h"

If you compile directly using go build, you might encounter an error like /tmp/go-build.../..._myctp_swig.go:20438:46: illegal rune literal.

This is because the automatically generated Go code contains lines like const THOST_FTDC_VTC_BankBankToFuture byte = '102001'. This is syntactically incorrect in Go, as strings should be enclosed in double quotes, not single quotes, and assigning a string to a byte variable is also problematic.

const THOST_FTDC_VTC_BankBankToFuture byte = '102001'
const THOST_FTDC_VTC_BankFutureToBank byte = '102002'
const THOST_FTDC_VTC_FutureBankToFuture byte = '202001'
const THOST_FTDC_VTC_FutureFutureToBank byte = '202002'

One way to resolve this is to modify the ThostFtdcUserApiDataType.h file by commenting out the relevant #define statements for these constants. While this prevents compilation errors, it means the corresponding constants won’t be defined in your Go code, preventing you from using functions that rely on them.

/////////////////////////////////////////////////////////////////////////
///TFtdcVirementTradeCodeType是一个交易代码类型
/////////////////////////////////////////////////////////////////////////
///银行发起银行资金转期货
//#define THOST_FTDC_VTC_BankBankToFuture '102001'
///银行发起期货资金转银行
//#define THOST_FTDC_VTC_BankFutureToBank '102002'
///期货发起银行资金转期货
//#define THOST_FTDC_VTC_FutureBankToFuture '202001'
///期货发起期货资金转银行
//#define THOST_FTDC_VTC_FutureFutureToBank '202002'

......

/////////////////////////////////////////////////////////////////////////
///TFtdcFBTTradeCodeEnumType是一个银期交易代码枚举类型
/////////////////////////////////////////////////////////////////////////
///银行发起银行转期货
//#define THOST_FTDC_FTC_BankLaunchBankToBroker '102001'
///期货发起银行转期货
//#define THOST_FTDC_FTC_BrokerLaunchBankToBroker '202001'
///银行发起期货转银行
//#define THOST_FTDC_FTC_BankLaunchBrokerToBank '102002'
///期货发起期货转银行
//#define THOST_FTDC_FTC_BrokerLaunchBrokerToBank '202002'

Additionally, the ThostFtdcMdApi.h file needs modification. In C++, char *ppInstrumentID[] and char **ppInstrumentID are equivalent, but after SWIG wrapping, char **ppInstrumentID compiles correctly in Go, whereas char *ppInstrumentID[] does not. Therefore, you should change the function signatures in the header file to use char **ppInstrumentID.

///订阅行情。
	///@param ppInstrumentID 合约ID  
	///@param nCount 要订阅/退订行情的合约个数
	///@remark 
	//virtual int SubscribeMarketData(char *ppInstrumentID[], int nCount) = 0;
        virtual int SubscribeMarketData(char **ppInstrumentID, int nCount) = 0;

	///退订行情。
	///@param ppInstrumentID 合约ID  
	///@param nCount 要订阅/退订行情的合约个数
	///@remark 
	//virtual int UnSubscribeMarketData(char *ppInstrumentID[], int nCount) = 0;
        virtual int UnSubscribeMarketData(char **ppInstrumentID, int nCount) = 0;
	
	///订阅询价。
	///@param ppInstrumentID 合约ID  
	///@param nCount 要订阅/退订行情的合约个数
	///@remark 
	//virtual int SubscribeForQuoteRsp(char *ppInstrumentID[], int nCount) = 0;
        virtual int SubscribeForQuoteRsp(char **ppInstrumentID, int nCount) = 0;

	///退订询价。
	///@param ppInstrumentID 合约ID  
	///@param nCount 要订阅/退订行情的合约个数
	///@remark 
	//virtual int UnSubscribeForQuoteRsp(char *ppInstrumentID[], int nCount) = 0;
        virtual int UnSubscribeForQuoteRsp(char **ppInstrumentID, int nCount) = 0;

Environment Setup

Ensure you have the necessary dependencies installed. For this example, the following versions were used:

  • Operating System: Ubuntu 22.04
  • Go Version: 1.21.1
  • SWIG Version: 4.0.2
  • g++ Version: 11.4.0

Compilation

Once everything is set up, you can compile using the following commands:

bash

export GOPATH=$GOPATH:'/home/netxianren/workspace/myctp'
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:'/home/netxianren/workspace/myctp/src/myctp'
cd /home/netxianren/workspace/myctp/src/myctp
go install -x -v -work

You can also use an IDE like LiteIDE and click the “Build” button to compile.

Using this method, the author has successfully compiled CTP versions 6.3.15, 6.3.16, 6.5.1, and 6.7.13.

Autocompletion in LiteIDE

To enable autocompletion in LiteIDE, you need the myctp.go file (generated by SWIG) in your package directory. However, this file can significantly increase compilation time. A workaround is to rename the file’s extension (e.g., to .go.txt) during compilation and change it back to .go afterward.

myctp.go could be generated by following command:

swig -c++ -go -cgo -intgosize 64 myctp.swigcxx

Deployment and Dynamic Library Loading

For normal deployment, you can copy the dynamic link library files (libthostmduserapi_se.so and libthosttraderapi_se.so) to a system library directory, such as /usr/lib on Ubuntu.

However, if you want to load the libraries from the same directory as your executable (allowing multiple programs on the same machine to use different CTP versions), you can modify the LD_LIBRARY_PATH environment variable.

Assuming your executable is named future and is located in /var, you can start it with the following command. This ensures future prioritizes loading the dynamic libraries from the /var directory if the dynamic link library files (libthostmduserapi_se.so and libthosttraderapi_se.so) are copied to /var directory .

LD_LIBRARY_PATH='/var':$LD_LIBRARY_PATH /var/future

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top