龙听期货论坛's Archiver

龙听 发表于 2024-9-26 15:31

鼎元C++程序化交易系统教程【test.cpp文件解读】

以一个套利策略全部程式码为例:[url]http://www.qhlt.cn/thread-158982-1-1.html[/url];

下面解构一下策略模板:

1、库引用:[code]#include "pch.h"
#include "test.h"
#include <vector>
#include <windows.h>
#include <fstream>
#include <stdio.h>
#include <mmsystem.h>
#include <thread>
#pragma comment(lib,"winmm.lib")
using namespace std;[/code]2、外部文件引用:[code]extern "C" __declspec(dllexport)Interface * create(HWND hw, string sName, string sPeriod, string sInst)
{
        return new test(hw, sName, sPeriod, sInst);
}
extern "C" __declspec(dllexport)void destory(Interface * p)
{
        delete p;
}[/code]3、细碎功能部分:

字段分割:[code]vector<std::string> split(std::string str, std::string pattern)
{
        std::string::size_type pos;
        std::vector<std::string> result;
        str += pattern;
        int size = str.size();
        for (int i = 0; i < size; i++)
        {
                pos = str.find(pattern, i);
                if (pos < size)
                {
                        std::string s = str.substr(i, pos - i);
                        result.push_back(s);
                        i = pos + pattern.size() - 1;
                }
        }
        return result;
}[/code]其它细碎功能[code]test::test(HWND hw, string sn, string sp, string si)
{
        hwnd = hw;
        sName = sn;
        sPeriod = sp;
        sInst = si;
        //  SetTimer(NULL, 0, 1000, (TIMERPROC)TimerProc);
}

test::~test()
{
}

void test::OnState(string s)
{
        state = s;
        TDLLDATA* t = new TDLLDATA();
        t->sName = sName;
        t->sType = "state";
        t->sData = s;
        if (hwnd)SendMessage(hwnd, WM_DLLDATA, (WPARAM)t, 10);
      
}
void test::UpdateParm(string s)
{
        vector<std::string> v = split(s, "@");
        for (int i = 0; i < v.size(); i++)
        {
                string ss = v.at(i);
                vector<std::string> vv = split(ss, "_");
                if (vv.size() >= 3)
                {
                        TDLLPARM t;
                        t.Name = vv.at(0); t.Value = vv.at(1); t.Explain = vv.at(2); tend(t);
                        parm[t.Name] = t;
                }
        }
}

void test::UpdateSub(string sub, double ratio)
{
        mapSub[sub] = ratio;
}

void test::end()
{
        string s;
        list<string>::iterator it;
        for (it = lst.begin(); it != lst.end(); ++it)
        {
                string key = *it;
                TDLLPARM t = parm[key];
                string ss = t.Name + "_" + t.Value + "_" + t.Explain;
                s = s + ss + "@";
        }
        TDLLDATA* t = new TDLLDATA();
        t->sName = sName;
        t->sType = "InitParm";
        t->sData = s;
        if (hwnd)SendMessage(hwnd, WM_DLLDATA, (WPARAM)t, 10);
}[/code][code]void test::tend(TDLLPARM t)
{
        lst.push_back(t.Name);
        parm[t.Name] = t;
}

void test::SubscribeMarketData(string s)
{
        TDLLDATA* t = new TDLLDATA();
        t->sName = sName;
        t->sType = "SubMd";
        t->sData = s;
        if (hwnd)SendMessage(hwnd, WM_SUBMD, (WPARAM)t, 11);
}
[/code]

龙听 发表于 2024-9-26 15:32

订单与交易及查询相关功能[code]void test::tend(TDLLPARM t)
{
        lst.push_back(t.Name);
        parm[t.Name] = t;
}

void test::SubscribeMarketData(string s)
{
        TDLLDATA* t = new TDLLDATA();
        t->sName = sName;
        t->sType = "SubMd";
        t->sData = s;
        if (hwnd)SendMessage(hwnd, WM_SUBMD, (WPARAM)t, 11);
}

bool test::IsbeigenOrdrSysID(string orderid)
{
        list<string>::iterator it;
        for (it = lstbeigenOrdrSysID.begin(); it != lstbeigenOrdrSysID.end(); it++)
        {
                string temp = *it;
                if (temp == orderid)return true;
        }
        return false;
}


bool test::IsActionOrdrSysID(string orderid)
{
        list<string>::iterator it;
        for (it = lstActionOrdrSysID.begin(); it != lstActionOrdrSysID.end(); it++)
        {
                string temp = *it;
                if (temp == orderid)return true;
        }
        return false;
}

void test::OrderInsert(string acc, string inst, char bs, char  oc, int vol, double price, string forfok, string ref2)
{
        TORDERINSERT* t = new TORDERINSERT();
        t->sName = sName;
        t->InvestorID = acc;
        t->InstrumentID = inst;
        t->BuySell = bs;
        t->OpenClose = oc;
        t->Volume = vol;
        t->LimitPrice = price;
        t->FakFok = forfok;
        t->Ref1 = sName;
        t->Ref2 = ref2;
        if (hwnd)SendMessage(hwnd, WM_INSERT, (WPARAM)t, 12);
}

void test::OrderAction(CThostFtdcOrderField t)
{
        TACTION* ta = new TACTION();
        ta->Name = sName;
        ta->InvestorID = t.InvestorID;
        ta->OrderSysID = t.OrderSysID;
        ta->BrokerID = t.BrokerID;
        ta->ExchangeID = t.ExchangeID;
        if (hwnd)SendMessage(hwnd, WM_ACTION, (WPARAM)ta, 13);
}

void test::RsqBar(string period, string inst)
{
        TRSQBAR* t = new TRSQBAR();
        t->Name = sName;
        t->Type = "RsqBar";
        t->Period = period;
        t->Inst = inst;
        if (hwnd)SendMessage(hwnd, WM_RSQBAR, (WPARAM)t, (LPARAM)36);
}

void test::InsertLog(string msg)
{
        TMSG* t = new TMSG();
        t->Name = sName;
        t->Msg = msg;
        if (hwnd)SendMessage(hwnd, WM_MSG, (WPARAM)t, 14);
}

void test::RsqInstrument(string inst)
{
        TRSQ* t = new TRSQ();
        t->Name = sName;
        t->Type = "RsqInstrument";
        t->Inst = inst;
        if (hwnd)SendMessage(hwnd, WM_RSQ, (WPARAM)t, (LPARAM)33);
}

void test::RsqRspQryOrder()
{
        TRSQ* t = new TRSQ();
        t->Name = sName;
        t->Type = "RspQryOrder";
        if (hwnd)SendMessage(hwnd, WM_RSQ, (WPARAM)t, (LPARAM)33);
}

void test::RsqRspQryTrade()
{
        TRSQ* t = new TRSQ();
        t->Name = sName;
        t->Type = "RspQryTrade";
        if (hwnd)SendMessage(hwnd, WM_RSQ, (WPARAM)t, (LPARAM)33);
}

void test::RsqPosition()
{
        TRSQ* t = new TRSQ();
        t->Name = sName;
        t->Type = "RsqPosition";
        if (hwnd)SendMessage(hwnd, WM_RSQ, (WPARAM)t, (LPARAM)33);
}

void test::RsqPositionDetail(string acc)
{
        TRSQ* t = new TRSQ();
        t->Name = sName;
        t->Type = "RsqPositionDetail";
        t->Account = acc;
        if (hwnd)SendMessage(hwnd, WM_RSQ, (WPARAM)t, (LPARAM)33);
}

void test::RsqCommissionRate()
{
        TRSQ* t = new TRSQ();
        t->Name = sName;
        t->Type = "RsqCommissionRate";
        if (hwnd)SendMessage(hwnd, WM_RSQ, (WPARAM)t, (LPARAM)33);
}

void test::RsqAccount()
{
        TRSQ* t = new TRSQ();
        t->Name = sName;
        t->Type = "RsqAccount";
        if (hwnd)SendMessage(hwnd, WM_RSQ, (WPARAM)t, (LPARAM)33);
}

void test::sound(string s)
{
        size_t size = s.length();
        wchar_t* buffer = new wchar_t[size + 1];
        MultiByteToWideChar(CP_ACP, 0, s.c_str(), size, buffer, size * sizeof(wchar_t));
        buffer[size] = 0;
        PlaySound(buffer, NULL, SND_FILENAME | SND_ASYNC);
        delete buffer;
        buffer = NULL;
}[/code]

龙听 发表于 2024-9-26 15:33

4、公式计算核心功能,类似mc中的function的功能。[code]//公式函数集合,类似MC中的function................................................................................................................................................
double test::avg(string period, string inst, int num)
{
        double d = 0;
        int n = 0;
        map<string, TKVALUE>::reverse_iterator it;
        //  InsertLog(to_string(mapK[period][inst].size()));
        for (it = mapK[period][inst].rbegin(); it != mapK[period][inst].rend(); ++it)
        {
                d += it->second.dClose;
                n++;
                if (n >= num)break;
        }
        return d / n;
}

double test::highest(string period, string inst, int num)
{
        double d = 0;
        int n = 0;
        map<string, TKVALUE>::reverse_iterator it;
        for (it = mapK[period][inst].rbegin(); it != mapK[period][inst].rend(); ++it)
        {
                if (d < it->second.dHigh)d = it->second.dHigh;
                n++;
                if (n >= num)break;
        }
        return d;
}

double test::lowerest(string period, string inst, int num)
{
        double d = 0;
        int n = 0;
        map<string, TKVALUE>::reverse_iterator it;
        for (it = mapK[period][inst].rbegin(); it != mapK[period][inst].rend(); ++it)
        {
                if (d == 0)d = it->second.dLow;
                if (d > it->second.dLow)d = it->second.dLow;
                n++;
                if (n >= num)break;
        }
        return d;
}


double test::avg1(string period, string inst, int num, int ref)
{
        double d = 0;
        int n = 0;
        int r = 0;
        map<string, TKVALUE>::reverse_iterator it;
        //  InsertLog(to_string(mapK[period][inst].size()));
        for (it = mapK[period][inst].rbegin(); it != mapK[period][inst].rend(); ++it)
        {
                r++;
                if (r <= ref)continue;
                d += it->second.dClose;
                n++;
                if (n >= num)break;
        }
        return d / n;
}

double test::highest1(string period, string inst, int num, int ref)
{
        double d = 0;
        int n = 0;
        int r = 0;
        map<string, TKVALUE>::reverse_iterator it;
        for (it = mapK[period][inst].rbegin(); it != mapK[period][inst].rend(); ++it)
        {
                r++;
                if (r <= ref)continue;
                if (d < it->second.dHigh)d = it->second.dHigh;
                n++;
                if (n >= num)break;
        }
        return d;
}

double test::lowerest1(string period, string inst, int num, int ref)
{
        double d = 0;
        int n = 0;
        int r = 0;
        map<string, TKVALUE>::reverse_iterator it;
        for (it = mapK[period][inst].rbegin(); it != mapK[period][inst].rend(); ++it)
        {
                r++;
                if (r <= ref)continue;
                if (d == 0)d = it->second.dLow;
                if (d > it->second.dLow)d = it->second.dLow;
                n++;
                if (n >= num)break;
        }
        return d;
}
//公式函数计算集合结束...................................................................................................................................................[/code]

龙听 发表于 2024-9-26 15:34

5、获取系统时间功能[code]string GetSystemTime()
{
        SYSTEMTIME m_time;
        GetLocalTime(&m_time);
        char szDateTime[100] = { 0 };
        sprintf_s(szDateTime, "%02d:%02d:%02d.:%03d", m_time.wHour, m_time.wMinute, m_time.wSecond, m_time.wMilliseconds);
        string time(szDateTime);
        return time;
}[/code]

龙听 发表于 2024-9-26 15:36

[color=Red][b]6、交易界面时间设置及面板中策略参数的获取功能[/b][/color]
示意图参考:[url]http://www.qhlt.cn/thread-158984-1-1.html[/url];[code]/// <summary>
/// 以下开始
/// </summary>
string timeStart = "08:50:00";
string timePause = "15:32:00";
string timeResume = "20:50:00";
string timeStop = "02:32:00";
string state = "stop";
void test::InitParm()
{
        TDLLPARM t;
        //Name 参数名, Value 默认值, Explain 参数说明, tend 表示一个参数结束
        t.Name = "第一腿合约"; t.Value = "hc2210"; t.Explain = "第一腿合约代码"; tend(t);
        t.Name = "第二腿合约"; t.Value = "rb2210"; t.Explain = "第二腿合约代码"; tend(t);
        t.Name = "第一腿买卖"; t.Value = "0"; t.Explain = "0位买入1位卖出"; tend(t);
        t.Name = "第一腿开平"; t.Value = "0"; t.Explain = "0为开仓1位平仓3位平今仓4位平昨仓"; tend(t);
        t.Name = "交易数量"; t.Value = "1"; t.Explain = "下单交易数量"; tend(t);
        t.Name = "条件价差"; t.Value = "100"; t.Explain = "条件价差入场值"; tend(t);
        t.Name = "条件价差方向"; t.Value = "1"; t.Explain = "1为大于,-1为小于"; tend(t);
        t.Name = "交易滑点值"; t.Value = "0"; t.Explain = "交易滑点值"; tend(t);
        t.Name = "是否重发"; t.Value = "1"; t.Explain = "0不重发,1重发"; tend(t);
        t.Name = "时间间隔"; t.Value = "5"; t.Explain = "委托后不成交撤单时间间隔 s "; tend(t);
        t.Name = "挂单量"; t.Value = "50"; t.Explain = "达到最小挂单量才下单"; tend(t);
        end();
}[/code]这里面的参数要和头文件里面的设置对应起来(最底部的var声明那里),头文件链接:[url=http://www.qhlt.cn/thread-158981-1-1.html]http://www.qhlt.cn/thread-158981-1-1.html[/url];
参数设置示意图:[img]http://p.algo2.net/2024/0926/a5fd5a3141ab9.png[/img]
一旦在交易面板中点了运行“按钮”[img]http://p.algo2.net/2024/0926/4a0bac7e110a1.png[/img]时加载的项目[code]void test::OnRun()
{
        OnState("run");
        mapPosDeta.clear();
        mapPos.clear();
        mapOrder.clear();
        mapTrade.clear();
        RsqRspQryOrder();
        RsqPosition();
        RsqRspQryTrade();
        RsqAccount();
        SubscribeMarketData(sInst);
        RsqBar(sPeriod, sInst);
        num = 0;
        sound("sound\\run.wav");
        OnState("run");
        dyt = parm["第一腿合约"].Value;
        det = parm["第二腿合约"].Value;
        bsfx = parm["第一腿买卖"].Value;
        ocfx = parm["第一腿开平"].Value;
        tjjc = atof(parm["条件价差"].Value.c_str());
        sl = atoi(parm["交易数量"].Value.c_str());
        hd = atof(parm["交易滑点值"].Value.c_str());
        gdl = atoi(parm["挂单量"].Value.c_str());
        sfcd = parm["是否重发"].Value.c_str();
        tjjcfx = parm["条件价差方向"].Value;
        sjjg = atoi(parm["时间间隔"].Value.c_str());
        SubscribeMarketData(dyt);
        SubscribeMarketData(det);
        RsqInstrument(dyt);
        RsqInstrument(det);
        yfd = false;
        dytcj = false;
        detcj = false;
        tz = false;
        InsertLog("  第一腿  " + dyt + "  第二退 " + det + "  买卖 " + bsfx + "  开平  " + ocfx + "   数量  " + to_string(sl)
                + "  条件价差  " + to_string(tjjc) + "   条件价差方向   " + tjjcfx + "   滑点  " + to_string(hd) + "  是否重发  " + sfcd + "   挂单量  " + to_string(gdl));
        InsertLog("    期货套利策略开始运行   ");
}[/code]点击交易界面中点击“停止”[img]http://p.algo2.net/2024/0926/3768ba3817af1.png[/img]按钮后加载项目[code]void test::OnStop()
{
        OnState("stop");
        sound("sound\\stop.wav");
        InsertLog("    期货套利策略停止运行   ");
}[/code]

龙听 发表于 2024-9-26 15:54

[color=Blue]7、核心策略设计(如何进场,如何出场,如何止盈,如何止损)[/color](因为程式码太长,另建贴存放,链接:[url]http://www.qhlt.cn/thread-158986-1-1.html[/url];)

龙听 发表于 2024-9-26 15:56

8、跟交易相关的发单,撤单,重发,优先平今或优先平昨等,跟策略相关不大,不建议改动(因为程式码太长,查看链接:[url]http://www.qhlt.cn/thread-158985-1-1.html[/url];)

龙听 发表于 2024-9-26 16:07

总结:

1、配置文件类程式码无须变动。

[color=Red]2、页头文件,公式计算(4),交易界面设置与变量参数设置(6),核心策略设计(7)是重点或者说经常变动部分,在平时的策略化交易过程中会不时的修改与设计。后面的策略教学中也以这三个部分为主。[/color]

页: [1]