Hablando de estrategias de cobertura, hay varios tipos, diversas combinaciones e ideas diversas en varios mercados. Exploramos las ideas y conceptos de diseño de la estrategia de cobertura desde la cobertura intertemporal más clásica. Hoy en día, el mercado de criptomonedas es mucho más activo que al principio, y también hay muchos intercambios de contratos de futuros que ofrecen muchas oportunidades para la cobertura de arbitraje.
¿Por qué la estrategia es algo hardcore porque la estrategia está escrita en C++ y la lectura de la estrategia es un poco más difícil? Pero no impide que los lectores aprendan la esencia de este diseño e ideas de estrategia. La lógica de la estrategia es relativamente simple, la longitud del código es moderada, solo 500 líneas. En términos de adquisición de datos de mercado, a diferencia de las otras estrategias que utilizan la interfaz
En términos de diseño, la estructura de la estrategia es razonable, el grado de acoplamiento del código es muy bajo, y es conveniente para expandir o optimizar. La lógica es clara, y tal diseño no solo es fácil de entender. Como material de enseñanza, aprender el diseño de esta estrategia también es un buen ejemplo.
Después de comprender los principios básicos, el resto es cómo la estrategia activa la posición de apertura de la cobertura, cómo cerrar la posición, cómo agregar posiciones, método de control de posición total y otros detalles de procesamiento de la estrategia.
La estrategia de cobertura se refiere principalmente a la fluctuación de la diferencia de precio del objeto (The Spread) y su regresión.
Esto trae incertidumbre sobre las ganancias y pérdidas de cobertura, pero el riesgo sigue siendo mucho menor que la tendencia unilateral. para las diversas optimizaciones de la estrategia intertemporal, podemos elegir comenzar desde el nivel de control de la posición y la condición de activación de apertura y cierre. por ejemplo, podemos usar el clásico
Mirando el código en su totalidad, se puede concluir que el código está dividido aproximadamente en cuatro partes.
Enumerar las definiciones de valores, definir algunos valores de estado, y utilizar para marcar estados. Algunas funciones funcionales que no están relacionadas con la estrategia, tales como funciones de codificación de url, funciones de conversión de tiempo, etc., no tienen relación con la lógica de la estrategia, sólo para el procesamiento de datos.
Clase generadora de datos de línea K: la estrategia está impulsada por los datos de línea K generados por el objeto de clase generadora.
Clase de cobertura: los objetos de esta clase pueden realizar una lógica comercial específica, operaciones de cobertura y procesar detalles de la estrategia.
La función principal de la estrategia, que es la función
A través de la comprensión general del código de estrategia, podemos aprender gradualmente los diversos aspectos de la estrategia, y luego estudiar el diseño, las ideas y las habilidades de la estrategia.
State
Declaraciónenum State { // Enum type defines some states
STATE_NA, // Abnormal state
STATE_IDLE, // idle
STATE_HOLD_LONG, // holding long positions
STATE_HOLD_SHORT, // holding short positions
};
Debido a que algunas funciones en el código devuelven un estado, estos estados se definen en el tipo de enumeraciónState
.
Viendo queSTATE_NA
aparece en el código es anormal, ySTATE_IDLE
está inactivo, es decir, el estado de la operación puede cubrirse.STATE_HOLD_LONG
es el estado en el que se mantiene la posición de cobertura positiva.STATE_HOLD_SHORT
Es el estado en el que se mantiene la posición de cobertura negativa.
string replace(string s, const string from, const string& to)
toHex
inline unsigned char toHex(unsigned char x)
std::string urlencode(const std::string& str)
uint64_t _Time(string &s)
class BarFeeder { // K line data generator class
public:
BarFeeder(int period) : _period(period) { // constructor with argument "period" period, initialized in initialization list
_rs.Valid = true; // Initialize the "Valid" property of the K-line data in the constructor body.
}
void feed(double price, chart *c=nullptr, int chartIdx=0) { // input data, "nullptr" null pointer type, "chartIdx" index default parameter is 0
uint64_t epoch = uint64_t(Unix() / _period) * _period * 1000; // The second-level timestamp removes the incomplete time period (incomplete _period seconds) and is converted to a millisecond timestamp.
bool newBar = false; // mark the tag variable of the new K line Bar
if (_rs.size() == 0 || _rs[_rs.size()-1].Time < epoch) { // if the K line data is 0 in length. Or the last bar's timestamp is less than epoch (the last bar of the K line is more than the current most recent cycle timestamp)
record r; // declare a K line bar structure
r.Time = epoch; // construct the K line bar of the current cycle
r.Open = r.High = r.Low = r.close = price; // Initialize the property
_rs.push_back(r); // K line bar is pressed into the K line data structure
if (_rs.size() > 2000) { // if the K-line data structure length exceeds 2000, the oldest data is removed.
_rs.erase(_rs.begin());
}
newBar = true; // tag
} else { // In other cases, it is not the case of a new bar.
record &r = _rs[_rs.size() - 1]; // Reference the data of the last bar in the data.
r.High = max(r.High, price); // The highest price update operation for the referenced data.
r.Low = min(r.Low, price); // The lowest price update operation for the referenced data.
r.close = price; // Update the closing price of the referenced data.
}
auto bar = _rs[_rs.size()-1]; // Take the last column data and assign it to the bar variable
json point = {bar.Time, bar.Open, bar.High, bar.Low, bar.close}; // construct a json type data
if (c != nullptr) { // The chart object pointer is not equal to the null pointer, do the following.
if (newBar) { // judge if the new Bar appears
c->add(chartIdx, point); // call the chart object member function add to insert data into the chart object (new k line bar)
c->reset(1000); // retain only 1000 bar of data
} else {
c->add(chartIdx, point, -1); // Otherwise update (not new bar), this point (update this bar).
}
}
}
records & get() { // member function, method for getting K line data.
Return _rs; // Returns the object's private variable _rs . (ie generated K-line data)
}
private:
int _period;
records _rs;
};
Esta clase es principalmente responsable de procesar los datos de tick adquiridos en una línea K de diferencia para impulsar la lógica de cobertura de la estrategia.
Algunos lectores pueden tener preguntas, ¿por qué usar datos de tick? ¿Por qué construir un generador de datos de línea K como este? ¿No es bueno usar datos de línea K directamente? Este tipo de pregunta se ha emitido en tres ráfagas. Cuando escribí algunas estrategias de cobertura, también hice un alboroto. Encontré la respuesta cuando escribí la
Los datos de la línea K de la diferencia entre los dos contratos son las estadísticas de cambio de precio de la diferencia en un cierto período. Por lo tanto, no es posible simplemente tomar los datos de la línea K de cada uno de los dos contratos para la resta y calcular la diferencia de cada dato en cada barra de la línea K. El error más obvio es, por ejemplo, el precio más alto y el precio más bajo de dos contratos, no necesariamente al mismo tiempo. Por lo tanto, el valor restado no tiene mucho sentido.
Por lo tanto, necesitamos usar datos de tick en tiempo real para calcular la diferencia en tiempo real y calcular el cambio de precio en un cierto período en tiempo real (es decir, el precio más alto, más bajo, abierto y cerrado en la columna de la línea K).
class Hedge { // Hedging class, the main logic of the strategy.
public:
Hedge() { // constructor
...
};
State getState(string &symbolA, depth &depthA, string &symbolB, depth &depthB) { // Get state, parameters: contract A name, contract A depth data, contract B name, contract B depth data
...
}
bool Loop(string &symbolA, depth &depthA, string &symbolB, depth &depthB, string extra="") { // Opening and closing position main logic
...
}
private:
vector<double> _addArr; // Hedging adding position list
string _state_desc[4] = {"NA", "IDLE", "LONG", "SHORT"}; // Status value Description
int _countOpen = 0; // number of opening positions
int _countcover = 0; // number of closing positions
int _lastcache = 0; //
int _hedgecount = 0; // number of hedging
int _loopcount = 0; // loop count (cycle count)
double _holdPrice = 0; // holding position price
BarFeeder _feederA = BarFeeder(DPeriod); // A contract Quote K line generator
BarFeeder _feederB = BarFeeder(DPeriod); // B contract Quote K line generator
State _st = STATE_NA; // Hedging type Object Hedging position status
string _cfgStr; // chart configuration string
double _holdAmount = 0; // holding position amount
bool _iscover = false; // the tag of whether to close the position
bool _needcheckOrder = true; // Set whether to check the order
chart _c = chart(""); // chart object and initialize
};
Debido a que el código es relativamente largo, algunas partes se omiten, esto es principalmente mostrar la estructura de esta clase de cobertura, el constructor de la función de cobertura se omite, principalmente para el propósito de la inicialización del objeto.
- ¿ Qué pasa?
Esta función se ocupa principalmente de la inspección de órdenes, la cancelación de órdenes, la detección de posiciones, el equilibrio de posiciones, etc. Debido a que en el proceso de operaciones de cobertura, es imposible evitar una sola etapa (es decir, un contrato se ejecuta, otro no lo es), si el examen se realiza en la lógica de la orden de colocación, y luego el procesamiento de la operación de reenvío de órdenes u operación de posición de cierre, la lógica de estrategia será caótica.
Así que al diseñar esta parte, tomé otra idea. si la operación de cobertura se activa, siempre y cuando la orden se coloca una vez, independientemente de si hay una cobertura de una sola pierna, el valor predeterminado es que la cobertura es exitosa, y luego el saldo de la posición se detecta en elgetState
La función y la lógica para procesar el saldo se tratarán de forma independiente.
El bucle
La lógica de negociación de la estrategia se encapsula en esta función, en la quegetState
Se llama, y el objeto generador de datos de línea K se utiliza para generar los datos de línea K de la diferencia (la propagación), y se realiza el juicio de apertura, cierre y adición de la lógica de posición.
void main() {
...
string realSymbolA = exchange.SetcontractType(symbolA)["instrument"]; // Get the A contract (this_week / next_week / quarter ), the real contract ID corresponding to the week, next week, and quarter of the OKEX futures contract.
string realSymbolB = exchange.SetcontractType(symbolB)["instrument"]; // ...
string qs = urlencode(json({{"op", "subscribe"}, {"args", {"futures/depth5:" + realSymbolA, "futures/depth5:" + realSymbolB}}}).dump()) ; // jSON encoding, url encoding for the parameters to be passed on the ws interface
Log("try connect to websocket"); // Print the information of the connection WS interface.
auto ws = Dial("wss://real.okex.com:10442/ws/v3|compress=gzip_raw&mode=recv&reconnect=true&payload="+qs); // call the FMZ API "Dial" function to acess the WS interface of OKEX Futures
Log("connect to websocket sucess");
depth depthA, depthB; // Declare two variables of the depth data structure to store the depth data of the A contract and the B contract
auto filldepth = [](json &data, depth &d) { // construct the code for the depth data with the json data returned by the interface.
d.Valid = true;
d.Asks.clear();
d.Asks.push_back({atof(string(data["asks"][0][0]).c_str()), atof(string(data["asks"][0][1]).c_str( ))});
d.Bids.clear();
d.Bids.push_back({atof(string(data["bids"][0][0]).c_str()), atof(string(data["bids"][0][1]).c_str( ))});
};
string timeA; // time string A
string timeB; // time string B
while (true) {
auto buf = ws.read(); // Read the data pushed by the WS interface
...
}
Después de que la estrategia se inicia, se ejecuta desde la función principal. En la inicialización de la función principal, la estrategia se suscribe al mercado de ticks de la interfaz del websocket. El trabajo principal de la función principal es construir un bucle principal que reciba continuamente las cotizaciones de ticks empujadas por la interfaz del websocket de la bolsa, y luego llama a la función miembro del objeto de la clase de cobertura: función de bucle.
Un punto a tener en cuenta es que el mercado de ticks mencionado anteriormente es en realidad la interfaz de datos de profundidad fina de los pedidos de suscripción, que es los datos del libro de pedidos para cada archivo. Sin embargo, la estrategia solo utiliza el primer archivo de datos, de hecho, es casi igual a los datos del mercado de ticks. La estrategia no utiliza los datos de otros archivos, ni utiliza el valor del pedido del primer archivo.
Echa un vistazo más de cerca a cómo la estrategia se suscribe a los datos de la interfaz websocket y cómo está configurado.
string qs = urlencode(json({{"op", "subscribe"}, {"args", {"futures/depth5:" + realSymbolA, "futures/depth5:" + realSymbolB}}}).dump());
Log("try connect to websocket");
auto ws = Dial("wss://real.okex.com:10442/ws/v3|compress=gzip_raw&mode=recv&reconnect=true&payload="+qs);
Log("connect to websocket sucess");
En primer lugar, la codificación de url del mensaje de suscripción json parámetro pasado por la interfaz suscrita, es decir, el valor de lapayload
Entonces un paso importante es llamar a la función de interfaz API de la plataforma FMZ QuantDial
La funciónDial
Aquí hacemos algunas configuraciones, dejemos que el objeto de control de conexión de websocket ws que se va a crear tenga reconexión automática de desconexión (el mensaje de suscripción todavía utiliza el valorqs
la cadena de lapayload
Parámetro), para lograr esta función, es necesario añadir configuración en la cadena de parámetros de laDial
function.
El comienzo de laDial
el parámetro de función es el siguiente:
wss://real.okex.com:10442/ws/v3
esta es la dirección de la interfaz de websocket a la que se necesita acceder, y está separada por
|Parameter name|description|
|-|-|
|compress|compress is compression mode, OKEX websocket interface uses gzip_raw this way, so it is set to gzip_raw|
|mode|Mode is mode, optional dual, send and recv three kind. Dual is bidirectional, sending compressed data and receiving compressed data. Send is to send compressed data. Recv receives the compressed data and decompresses it locally.|
|reconnect|Reconnect is set to reconnect, reconnect=true to enable reconnection, no default is not reconnected.|
|payload|The payload is a subscription message that needs to be sent when ws is reconnected.|
After this setting, even if the websocket connection is disconnected, FMZ Quant trading platform's underlying system of the docker system will automatically reconnect and get the latest market data in time.
Grab every price fluctuation and quickly capture the right hedge.
- Position control
Position control is controlled using a ratio of hedge positions similar to the “Fibonaci” series.
para (int i = 0; i < AddMax + 1; i++) { // construir una estructura de datos que controle el número de scalping, similar a la relación de la secuencia de Bofinac con el número de coberturas.
si (_addArr.size() < 2) { // Las dos primeras posiciones añadidas se cambian como: el doble del número de cobertura
_addArr.push_back (((i+1) *OpenAmount); el número de los archivos que se pueden añadir
¿ Por qué?
_addArr.push_back ((_addArr[_addArr.size()-1] + _addArr[_addArr.size()-2]); // Las dos últimas posiciones de sumación se suman juntas, y la cantidad de posición actual se calcula y se almacena en la estructura de datos
It can be seen that the number of additional positions added each time is the sum of the last two positions.
Such position control can realize the larger the difference, the relative increase of the arbitrage hedge, and the dispersion of the position, so as to grasp the small position of the small price fluctuation, and the large price fluctuation position is appropriately increased.
- closing position: stop loss and take profit
Fixed stop loss spread and take profit spread.
When the position difference reaches the take profit position and the stop loss position, the take profit and stop loss are carried out.
- The designing of entering the market and leaving the market
The period of the parameter ```NPeriod``` control provides some dynamic control over the opening and closing position of the strategy.
- Strategy chart
The strategy automatically generates a spread K-line chart to mark relevant transaction information.
c++ strategy custom chart drawing operation is also very simple. You can see that in the constructor of the hedge class, we use the written chart configuration string ```_cfgStr``` to configure the chart object ```_c```, ```_c``` is the private component of the hedge class. When the private member is initialized, the ```chart``` object constructed by the FMZ Quant platform custom chart API interface function is called.
_cfgStr = R
llamada _c.update(_cfgStr); Utilice _cfgStr para configurar el objeto del gráfico.
llamada _c.reset(); para restablecer los datos del gráfico.
When the strategy code needs to insert data into the chart, it also calls the member function of the ```_c``` object directly, or passes the reference of ```_c``` as a parameter, and then calls the object member function (method) of ```_c``` to update the chart data and insert operation.
E.g:
_c.add(chartIdx, {{
After placing the order, mark the K line chart.
As follows, when drawing a K line, a reference to the chart object ```_c``` is passed as a parameter when calling the member function ```feed``` of the ```BarFeeder``` class.
el valor de la fuente de alimentación no válida ((precio doble, gráfico *c=nullptr, gráfico intIdx=0)
That is, the formal parameter ```c``` of the ```feed``` function.
punto json = {bar.Time, bar.Open, bar.High, bar.Low, bar.close}; // construir un tipo de datos json
si (c!= nullptr) { // El puntero del objeto del gráfico no es igual al puntero nulo, haga lo siguiente.
si (newBar) { // juzgar si aparece la nueva barra
c->add(chartIdx, punto); // llamar a la función miembro del objeto del gráfico
Insert a new K-line Bar data into the chart by calling the ```add``` member function of the chart object ```_c```.
c-> añadir ((gráficoIdx, punto);
Esta estrategia es sólo para fines de aprendizaje y comunicación. Cuando se aplique en el mercado real, modifique y optimice según la situación real del mercado.
Dirección estratégica:https://www.fmz.com/strategy/163447
Las estrategias más interesantes están en la plataforma FMZ Quant