Ver Fonte

新增manager版本

KarsusNeko há 6 anos atrás
pai
commit
76d3d4a7f2

+ 48 - 0
MT5MonkMAM/MT5MonkMAM.cpp

@@ -0,0 +1,48 @@
+// MT5MonkMAM.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
+//
+
+#include "pch.h"
+#include <iostream>
+
+#include <ini/Settings.h>
+
+int main()
+{
+	std::shared_ptr<tools::logger> logger(new tools::logger("MT5MonkMAM.log", tools::logger::FILE | tools::logger::SCREEN));
+
+	logger->set_level(tools::logger::LVTRACK);
+
+	Settings set("MT5MonkMAM.ini");
+	Dictionary dict = set.GetSection("MT5");
+	std::string server = dict.GetString("Server");
+	UINT64 login = dict.GetInt("Login");
+	std::string password = dict.GetString("Password");
+
+	dict = set.GetSection("MAM");
+	UINT64 trader = dict.GetInt("Trader");
+	std::string groups = dict.GetString("Groups");
+	UINT step = dict.GetInt("Step");
+	UINT tolerance = dict.GetInt("Tolerance");
+
+	CManagerExtension manager;
+	manager.SetTrader(trader);
+	manager.SetGroups(groups);
+	manager.SetStep(step);
+	manager.SetTolerance(tolerance);
+
+	manager.SetLogger(logger);
+
+	CMTStr128 srv;
+	CMTStr128 pass;
+	srv.Assign(s2ws(server).c_str());
+	pass.Assign(s2ws(password).c_str());
+	manager.Initialize();
+	manager.Connect(srv, login, pass);
+
+	//std::cout << "Connected" << std::endl;
+
+	getchar();
+
+	return 0;
+}
+

+ 183 - 0
MT5MonkMAM/MT5MonkMAM.vcxproj

@@ -0,0 +1,183 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <VCProjectVersion>15.0</VCProjectVersion>
+    <ProjectGuid>{BB2691DA-EC5C-4CDC-A1D1-8759B1182871}</ProjectGuid>
+    <Keyword>Win32Proj</Keyword>
+    <RootNamespace>MT5MonkMAM</RootNamespace>
+    <WindowsTargetPlatformVersion>10.0.17763.0</WindowsTargetPlatformVersion>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v141</PlatformToolset>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v141</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v141</PlatformToolset>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v141</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="Shared">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <LinkIncremental>false</LinkIncremental>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <LinkIncremental>true</LinkIncremental>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <LinkIncremental>true</LinkIncremental>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <LinkIncremental>false</LinkIncremental>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <PrecompiledHeader>Use</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <ConformanceMode>true</ConformanceMode>
+      <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
+      <AdditionalIncludeDirectories>..\MT5SDK;..\CommonLib\CommonLib;..\cpp_redis\include</AdditionalIncludeDirectories>
+      <BufferSecurityCheck>false</BufferSecurityCheck>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalLibraryDirectories>..\cpp_redis\lib\x64\Release</AdditionalLibraryDirectories>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>Use</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <SDLCheck>true</SDLCheck>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <ConformanceMode>true</ConformanceMode>
+      <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <PrecompiledHeader>Use</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <SDLCheck>true</SDLCheck>
+      <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <ConformanceMode>true</ConformanceMode>
+      <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
+      <AdditionalIncludeDirectories>..\MT5SDK;..\CommonLib\CommonLib;</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>Use</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <ConformanceMode>true</ConformanceMode>
+      <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClInclude Include="ManagerExtension.h" />
+    <ClInclude Include="pch.h" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\CommonLib\CommonLib\ini\Dictionary.cpp">
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
+    </ClCompile>
+    <ClCompile Include="..\CommonLib\CommonLib\ini\Settings.cpp">
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
+    </ClCompile>
+    <ClCompile Include="..\CommonLib\CommonLib\log\logger.cpp">
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
+    </ClCompile>
+    <ClCompile Include="ManagerExtension.cpp" />
+    <ClCompile Include="MT5MonkMAM.cpp" />
+    <ClCompile Include="pch.cpp">
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
+    </ClCompile>
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>

+ 51 - 0
MT5MonkMAM/MT5MonkMAM.vcxproj.filters

@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="源文件">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="头文件">
+      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+      <Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>
+    </Filter>
+    <Filter Include="资源文件">
+      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+    </Filter>
+    <Filter Include="ini">
+      <UniqueIdentifier>{cbb8d368-70f6-4fa2-9d32-799afacbd17d}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="log">
+      <UniqueIdentifier>{5dab0da4-8966-4982-b22c-1644e963a758}</UniqueIdentifier>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="pch.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
+    <ClInclude Include="ManagerExtension.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="pch.cpp">
+      <Filter>源文件</Filter>
+    </ClCompile>
+    <ClCompile Include="MT5MonkMAM.cpp">
+      <Filter>源文件</Filter>
+    </ClCompile>
+    <ClCompile Include="ManagerExtension.cpp">
+      <Filter>源文件</Filter>
+    </ClCompile>
+    <ClCompile Include="..\CommonLib\CommonLib\ini\Dictionary.cpp">
+      <Filter>ini</Filter>
+    </ClCompile>
+    <ClCompile Include="..\CommonLib\CommonLib\ini\Settings.cpp">
+      <Filter>ini</Filter>
+    </ClCompile>
+    <ClCompile Include="..\CommonLib\CommonLib\log\logger.cpp">
+      <Filter>log</Filter>
+    </ClCompile>
+  </ItemGroup>
+</Project>

+ 7 - 0
MT5MonkMAM/MT5MonkMAM.vcxproj.user

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <LocalDebuggerWorkingDirectory>$(OutDir)</LocalDebuggerWorkingDirectory>
+    <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
+  </PropertyGroup>
+</Project>

+ 383 - 0
MT5MonkMAM/ManagerExtension.cpp

@@ -0,0 +1,383 @@
+#include "pch.h"
+
+CManagerExtension::CManagerExtension()
+	: m_manager(nullptr)
+	, m_manager_pumping(nullptr)
+	, m_run(false)
+{
+}
+
+CManagerExtension::~CManagerExtension()
+{
+	Shutdown();
+}
+
+bool CManagerExtension::Initialize()
+{
+	Shutdown();
+	//---    
+	MTAPIRES retcode = MT_RET_ERROR;
+	UINT     version = 0;
+	//--- Manage Factory init
+	if ((retcode = m_factory.Initialize()) != MT_RET_OK)
+	{
+		LogOut(tools::logger::LVFATAL, "ManagerExtension: loading manager API  failed [%u]\n", retcode);
+		return false;
+	}
+	//--- check Manager API version
+	if ((retcode = m_factory.Version(version)) != MT_RET_OK)
+	{
+		LogOut(tools::logger::LVFATAL, "ManagerExtension: getting version failed [%u]\n", retcode);
+		return false;
+	}
+	//--- show version  
+	wprintf(L"ManagerExtension: Manager API version %u has been loaded\n", version);
+	if (version < MTManagerAPIVersion)
+	{
+		LogOut(tools::logger::LVFATAL, "ManagerExtension: wrong Manager API version, version %u required\n", MTManagerAPIVersion);
+		return false;
+	}
+	//--- create manager interface
+	if ((retcode = m_factory.CreateManager(MTManagerAPIVersion, &m_manager)) != MT_RET_OK)
+	{
+		LogOut(tools::logger::LVFATAL, "ManagerExtension: creating manager interface failed [%u]\n", retcode);
+		return false;
+	}
+	if ((retcode = m_factory.CreateManager(MTManagerAPIVersion, &m_manager_pumping)) != MT_RET_OK)
+	{
+		LogOut(tools::logger::LVFATAL, "ManagerExtension: creating manager interface failed [%u]\n", retcode);
+		return false;
+	}
+	//if ((retcode = m_factory.CreateAdmin(MTManagerAPIVersion, &m_admin)) != MT_RET_OK)
+	//{
+	//	wprintf_s(L"AministratorExtension: creating admin interface failed [%u]\n", retcode);
+	//}
+	if ((retcode = m_manager->Subscribe(this)) != MT_RET_OK)
+	{
+		LogOut(tools::logger::LVFATAL, "ManagerExtension: manager subscribe failed [%u]\n", retcode);
+		return false;
+	}
+	if ((retcode = m_manager_pumping->Subscribe(this)) != MT_RET_OK)
+	{
+		LogOut(tools::logger::LVFATAL, "ManagerExtension: manager subscribe failed [%u]\n", retcode);
+		return false;
+	}
+
+	if ((retcode = m_manager_pumping->OrderSubscribe(this)) != MT_RET_OK)
+	{
+		LogOut(tools::logger::LVFATAL, "ManagerExtension: order subscribe failed [%u]\n", retcode);
+		return false;
+	}
+	//m_admin->Subscribe(this);
+	//--- ok
+	return true;
+}
+
+void CManagerExtension::Shutdown()
+{
+	//--- disconnect from server
+	Disconnect();
+	//--- check and release manager interface     
+	if (m_manager)
+	{
+		m_manager->Release();
+		m_manager = nullptr;
+	}
+	if (m_manager_pumping)
+	{
+		m_manager_pumping->Release();
+		m_manager_pumping = nullptr;
+	}
+	//if (m_admin)
+	//{
+	//	m_admin->Release();
+	//	m_admin = NULL;
+	//}
+	//--- unload Manager API
+	m_factory.Shutdown();
+
+	m_run = false;
+	if (m_work_thread.joinable())
+		m_work_thread.join();
+}
+
+bool CManagerExtension::Connect(const CMTStr & server, const UINT64 login, const CMTStr & password)
+{
+	//--- Disconnect if reconnect
+	Disconnect();
+	//---    
+	MTAPIRES retcode = MT_RET_ERROR;
+	//--- check input params
+	if (server.Empty() || login == 0 || password.Empty())
+	{
+		LogOut(tools::logger::LVFATAL, "ManagerExtension: connection failed with bad arguments\n");
+		return false;
+	}
+	//--- initialize Manger API
+	if (!m_manager)
+		return false;
+	if ((retcode = m_manager->Connect
+		(
+			server.Str(),
+			login,
+			password.Str(),
+			L"",
+			0,
+			CONNECTION_TIMEOUT
+		)) != MT_RET_OK)
+	{
+		LogOut(tools::logger::LVFATAL, "Connection failed [%u]\n", retcode);
+		return false;
+	}
+	if (!m_manager_pumping)
+		return false;
+	if ((retcode = m_manager_pumping->Connect
+		(
+			server.Str(),
+			login,
+			password.Str(),
+			L"",
+			IMTManagerAPI::PUMP_MODE_ORDERS,
+			CONNECTION_TIMEOUT
+		)) != MT_RET_OK)
+	{
+		LogOut(tools::logger::LVFATAL, "Connection failed [%u]\n", retcode);
+		return false;
+	}
+	//if ((retcode = m_admin->Connect(server.Str(), login, password.Str(), L"", 0, CONNECTION_TIMEOUT)) != MT_RET_OK)
+	//{
+	//	LogOut(tools::logger::LVFATAL, "Connection failed [%u]\n", retcode);
+	//	return false;
+	//}
+	//--- check and create in/out streams   
+	//if (m_in_stream == NULL)  m_in_stream = m_manager->CustomCreateStream();
+	//if (m_out_stream == NULL) m_out_stream = m_manager->CustomCreateStream();
+	//if (m_in_stream == NULL || m_out_stream == NULL)
+	//{
+	//	wprintf_s(L"ManagerExtension: CustomCreateStream failed\n");
+	//	return(false);
+	//}
+
+	m_run = true;
+
+	if(!m_work_thread.joinable())
+		m_work_thread = std::thread(&CManagerExtension::keep_alive, this);
+	//--- ok
+	return true;
+}
+
+void CManagerExtension::Disconnect()
+{
+	//--- if connection sets
+	if (m_manager)
+	{
+		m_manager->Disconnect();
+	}
+	if (m_manager_pumping)
+	{
+		m_manager_pumping->Disconnect();
+	}
+	//if (m_admin)
+	//{
+	//	m_admin->Disconnect();
+	//}
+}
+
+//void CManagerExtension::OnOrderAdd(const IMTOrder * order)
+//{
+//	if (order == nullptr)	return;
+//	if (order->Login() != m_trader)	return;
+//
+//	LogOut(tools::logger::LVTRACK, "OnOrderAdd");
+//}
+
+void CManagerExtension::OnOrderDelete(const IMTOrder * order)
+{
+	if (order == nullptr)	return;
+	if (order->Login() != m_trader)	return;
+
+	LogOut(tools::logger::LVTRACK, "OnOrderDelete");
+}
+
+void CManagerExtension::OnConnect()
+{
+	LogOut(tools::logger::LVLOG, "Server connected");
+}
+
+void CManagerExtension::OnDisconnect()
+{
+	LogOut(tools::logger::LVLOG, "Server lost connection");
+}
+
+void CManagerExtension::SetTrader(const UINT64 trader)
+{
+	m_trader = trader;
+}
+
+void CManagerExtension::SetGroups(const std::string & groups)
+{
+	m_groups = groups;
+}
+
+void CManagerExtension::SetStep(const int step)
+{
+	m_step = step;
+}
+
+void CManagerExtension::SetTolerance(const int tolerance)
+{
+	m_tolerance = tolerance;
+}
+
+void CManagerExtension::SetLogger(const std::shared_ptr<tools::logger>& logger)
+{
+	m_logger = logger;
+}
+
+void CManagerExtension::LogOut(const tools::logger::log_level level, const char * msg, ...)
+{
+	if (m_logger == nullptr)	return;
+
+	char buffer[1024];
+	//--- format string
+	va_list arg_ptr;
+	va_start(arg_ptr, msg);
+	_vsnprintf(buffer, sizeof(buffer) - 1, msg, arg_ptr);
+	va_end(arg_ptr);
+	//--- out to server log
+	m_logger->write(level, buffer);
+}
+
+bool CManagerExtension::start_redis()
+{
+	try
+	{
+		m_redis_error_notified = false;
+
+
+		if (m_redis_client)
+		{
+			if (m_redis_client->is_connected())
+				return true;
+		}
+
+		m_redis_client.reset(new cpp_redis::client);
+
+		m_redis_client->connect
+		(
+			m_redis_server,
+			m_redis_port,
+			[this](const std::string& host, std::size_t port, cpp_redis::connect_state status)
+			{
+				if (status == cpp_redis::connect_state::ok)
+				{
+					LogOut(tools::logger::LVTRACK, "redis server connected");
+				}
+			},
+			500,
+			10000000,
+			1000
+		);
+
+		if (m_redis_password != "")
+		{
+			auto fut = m_redis_client->auth(m_redis_password);
+			m_redis_client->sync_commit();
+			auto reply = fut.get();
+			if (reply.is_error())
+			{
+				LogOut(tools::logger::LVERROR, "connect: authentication failed");
+				return false;
+			}
+		}
+	}
+	catch (tacopie::tacopie_error& e)
+	{
+		LogOut(tools::logger::LVERROR, "redis conn: %s", e.what());
+		return false;
+	}
+	catch (std::exception& e)
+	{
+		LogOut(tools::logger::LVERROR, "redis conn: %s", e.what());
+		return false;
+	}
+	catch (...)
+	{
+		LogOut(tools::logger::LVERROR, "failed to connect to server");
+		return false;
+	}
+
+	return true;
+}
+
+void CManagerExtension::stop_redis()
+{
+	try
+	{
+		if (m_redis_client)
+		{
+			// ʹÓÃsync commit±£Ö¤²Ù×÷È«²¿Ìá½»
+			m_redis_client->sync_commit();
+			m_redis_client->disconnect();
+			m_redis_client.reset();
+		}
+	}
+	catch (tacopie::tacopie_error& e)
+	{
+		LogOut(tools::logger::LVERROR, "stop redis: %s", e.what());
+	}
+	catch (std::exception& e)
+	{
+		LogOut(tools::logger::LVERROR, "stop redis: %s", e.what());
+	}
+	catch (...)
+	{
+		LogOut(tools::logger::LVERROR, "stop redis: unknown error");
+	}
+}
+
+void CManagerExtension::keep_alive()
+{
+	using namespace std::chrono_literals;
+	int counter = 500;
+
+	while (m_run)
+	{
+		if (counter-- <= 0)
+		{
+			counter = 500;
+
+			std::lock_guard<decltype(m_lock)> lk(m_lock);
+			if (m_redis_client)
+			{
+				if (m_redis_client->is_connected())
+				{
+					m_redis_client->ping([this](cpp_redis::reply& r)
+					{
+						if (r.ko())
+						{
+							m_redis_error_notified = true;
+							if (!m_redis_error_notified)
+							{
+								LogOut(tools::logger::LVERROR, "redis: %s", r.error().c_str());
+							}
+						}
+					});
+				}
+				else
+				{
+					m_redis_error_notified = true;
+					if (!m_redis_error_notified)
+					{
+						LogOut(tools::logger::LVERROR, "redis server not connected");
+					}
+
+					stop_redis();
+					start_redis();
+				}
+			}
+		}
+
+		std::this_thread::sleep_for(16ms);
+	}
+}

+ 78 - 0
MT5MonkMAM/ManagerExtension.h

@@ -0,0 +1,78 @@
+#pragma once
+
+#include <memory>
+#include <mutex>
+
+#include <cpp_redis/cpp_redis>
+#include <WinSock2.h>
+
+#pragma comment(lib, "ws2_32.lib")
+#pragma comment(lib, "tacopie.lib")
+#pragma comment(lib, "cpp_redis.lib")
+
+#include <log/logger.h>
+
+#include "Misc.h"
+
+class CManagerExtension
+	: public IMTManagerSink
+	, public IMTOrderSink
+{
+	//--- constants     
+	enum
+	{
+		CONNECTION_TIMEOUT = 30000,
+	};
+
+private:
+	CMTManagerAPIFactory m_factory;        // Manager API factory
+	IMTManagerAPI    *m_manager;           // Manager interface  
+	IMTManagerAPI    *m_manager_pumping;   // Manager interface(pumping mode)  
+	//IMTAdminAPI		*m_admin;			  // Administrator API
+
+public:
+	CManagerExtension();
+	~CManagerExtension();
+	//--- init and shutdown
+	bool              Initialize();
+	void              Shutdown();
+	//--- connect, disconnect
+	bool              Connect(const CMTStr &server, const UINT64 login, const CMTStr &password);
+	void              Disconnect();
+
+	virtual void      OnOrderAdd(const IMTOrder* order);
+	virtual void      OnOrderDelete(const IMTOrder* order);
+
+	virtual void		 OnConnect();
+	virtual void		 OnDisconnect();
+
+	void			SetTrader(const UINT64 trader);
+	void			SetGroups(const std::string& groups);
+	void			SetStep(const int step);
+	void			SetTolerance(const int tolerance);
+
+	void			SetLogger(const std::shared_ptr<tools::logger>& logger);
+	void			  LogOut(const tools::logger::log_level level, const char* log, ...);
+private:
+	std::shared_ptr<tools::logger>	m_logger;
+	std::mutex m_lock;
+	std::atomic<bool>	m_run;
+
+	std::shared_ptr<cpp_redis::client>	m_redis_client;
+	bool		m_redis_conn;
+	bool		start_redis();
+	void		stop_redis();
+	void		keep_alive();
+	bool		m_redis_error_notified;
+	std::thread m_work_thread;
+
+	std::string		m_redis_server;
+	std::string		m_redis_password;
+	int				m_redis_port;
+
+	std::string		m_groups;
+	//std::wstring	m_logins;
+	UINT64			m_trader;
+	int				m_step;
+	int				m_tolerance;
+};

+ 210 - 0
MT5MonkMAM/Misc.h

@@ -0,0 +1,210 @@
+#pragma once
+
+#include <string>
+#include <comutil.h>
+#pragma comment(lib, "comsupp.lib")
+#pragma comment(lib, "comsuppw.lib")
+
+inline std::string ws2s(const std::wstring& ws)
+{
+	_bstr_t t = ws.c_str();
+	char* pchar = (char*)t;
+	return std::string(pchar);
+}
+inline std::wstring s2ws(const std::string& s)
+{
+	_bstr_t t = s.c_str();
+	wchar_t* pwchar = (wchar_t*)t;
+	return std::wstring(pwchar);
+}
+
+inline int CheckTemplate(char* expr, char* tok_end, const char* group, char* prev, int* deep)
+{
+	char  tmp = 0;
+	char *lastwc, *prev_tok;
+	const char *cp;
+	//--- 
+	if ((*deep)++ >= 10) return(FALSE);
+	//--- 
+	while (*expr == '*' && expr != tok_end) expr++;
+	if (expr == tok_end) return(TRUE);
+	//--- 
+	lastwc = expr;
+	while (*lastwc != '*' && *lastwc != 0) lastwc++;
+	//---
+	if ((tmp = *(lastwc)) != 0) //
+	{
+		tmp = *(lastwc); *(lastwc) = 0;
+		if ((prev_tok = (char*)strstr(group, expr)) == NULL)
+		{
+			if (tmp != 0) *(lastwc) = tmp;
+			return(FALSE);
+		}
+		*(lastwc) = tmp;
+	}
+	else //
+	{
+		//---
+		cp = group + strlen(group);
+		for (; cp >= group; cp--)
+			if (*cp == expr[0] && strcmp(cp, expr) == 0)
+				return(TRUE);
+		return(FALSE);
+	}
+	//---
+	if (prev != NULL && prev_tok <= prev) return(FALSE);
+	prev = prev_tok;
+	//---
+	group = prev_tok + (lastwc - expr - 1);
+	//---
+	if (lastwc != tok_end) return CheckTemplate(lastwc, tok_end, group, prev, deep);
+	//---
+	return(TRUE);
+}
+
+inline int CheckTemplate(wchar_t* expr, wchar_t* tok_end, const wchar_t* group, wchar_t* prev, int* deep)
+{
+	wchar_t  tmp = 0;
+	wchar_t *lastwc, *prev_tok;
+	const wchar_t *cp;
+	//--- 
+	if ((*deep)++ >= 10) return(FALSE);
+	//--- 
+	while (*expr == L'*' && expr != tok_end) expr++;
+	if (expr == tok_end) return(TRUE);
+	//--- 
+	lastwc = expr;
+	while (*lastwc != L'*' && *lastwc != 0) lastwc++;
+	//---
+	if ((tmp = *(lastwc)) != 0) //
+	{
+		tmp = *(lastwc); *(lastwc) = 0;
+		if ((prev_tok = (wchar_t*)wcsstr(group, expr)) == NULL)
+		{
+			if (tmp != 0) *(lastwc) = tmp;
+			return(FALSE);
+		}
+		*(lastwc) = tmp;
+	}
+	else //
+	{
+		//---
+		cp = group + wcslen(group);
+		for (; cp >= group; cp--)
+			if (*cp == expr[0] && wcscmp(cp, expr) == 0)
+				return(TRUE);
+		return(FALSE);
+	}
+	//---
+	if (prev != NULL && prev_tok <= prev) return(FALSE);
+	prev = prev_tok;
+	//---
+	group = prev_tok + (lastwc - expr - 1);
+	//---
+	if (lastwc != tok_end) return CheckTemplate(lastwc, tok_end, group, prev, deep);
+	//---
+	return(TRUE);
+}
+
+inline int CheckGroup(char* grouplist, const char *group)
+{
+	//--- 
+	if (grouplist == NULL || group == NULL) return(FALSE);
+	char *tok_start = grouplist, end;
+	int  res = TRUE, deep = 0, normal_mode;
+	while (*tok_start != 0)
+	{
+		//--- 
+		while (*tok_start == ',') tok_start++;
+		//---
+		if (*tok_start == '!') { tok_start++; normal_mode = FALSE; }
+		else                 normal_mode = TRUE;
+		//--- 
+		char *tok_end = tok_start;
+		while (*tok_end != ',' && *tok_end != 0) tok_end++;
+		end = *tok_end; *tok_end = NULL;
+		//---
+		char *tp = tok_start;
+		const char *gp = group;
+		char *prev = NULL;
+		res = TRUE;
+		while (tp != tok_end && *gp != NULL)
+		{
+			if (*tp == '*')
+			{
+				deep = 0;
+				if ((res = CheckTemplate(tp, tok_end, gp, prev, &deep)) == TRUE)
+				{
+					*tok_end = end;
+					return(normal_mode);
+				}
+				break;
+			}
+			if (*tp != *gp)
+			{
+				*tok_end = end;
+				res = FALSE;
+				break;
+			}
+			tp++;
+			gp++;
+		}
+		*tok_end = end;
+		if (*gp == NULL && (tp == tok_end || *tp == '*') && res == TRUE) return(normal_mode);
+		if (*tok_end == 0) break;
+		tok_start = tok_end + 1;
+	}
+	return(FALSE);
+}
+
+
+inline int CheckGroup(wchar_t* grouplist, const wchar_t *group)
+{
+	//--- 
+	if (grouplist == NULL || group == NULL) return(FALSE);
+	wchar_t *tok_start = grouplist, end;
+	int  res = TRUE, deep = 0, normal_mode;
+	while (*tok_start != 0)
+	{
+		//--- 
+		while (*tok_start == L',') tok_start++;
+		//---
+		if (*tok_start == L'!') { tok_start++; normal_mode = FALSE; }
+		else                 normal_mode = TRUE;
+		//--- 
+		wchar_t *tok_end = tok_start;
+		while (*tok_end != L',' && *tok_end != 0) tok_end++;
+		end = *tok_end; *tok_end = NULL;
+		//---
+		wchar_t *tp = tok_start;
+		const wchar_t *gp = group;
+		wchar_t *prev = NULL;
+		res = TRUE;
+		while (tp != tok_end && *gp != NULL)
+		{
+			if (*tp == L'*')
+			{
+				deep = 0;
+				if ((res = CheckTemplate(tp, tok_end, gp, prev, &deep)) == TRUE)
+				{
+					*tok_end = end;
+					return(normal_mode);
+				}
+				break;
+			}
+			if (*tp != *gp)
+			{
+				*tok_end = end;
+				res = FALSE;
+				break;
+			}
+			tp++;
+			gp++;
+		}
+		*tok_end = end;
+		if (*gp == NULL && (tp == tok_end || *tp == L'*') && res == TRUE) return(normal_mode);
+		if (*tok_end == 0) break;
+		tok_start = tok_end + 1;
+	}
+	return(FALSE);
+}

+ 5 - 0
MT5MonkMAM/pch.cpp

@@ -0,0 +1,5 @@
+// pch.cpp: 与预编译标头对应的源文件;编译成功所必需的
+
+#include "pch.h"
+
+// 一般情况下,忽略此文件,但如果你使用的是预编译标头,请保留它。

+ 19 - 0
MT5MonkMAM/pch.h

@@ -0,0 +1,19 @@
+// 入门提示: 
+//   1. 使用解决方案资源管理器窗口添加/管理文件
+//   2. 使用团队资源管理器窗口连接到源代码管理
+//   3. 使用输出窗口查看生成输出和其他消息
+//   4. 使用错误列表窗口查看错误
+//   5. 转到“项目”>“添加新项”以创建新的代码文件,或转到“项目”>“添加现有项”以将现有代码文件添加到项目
+//   6. 将来,若要再次打开此项目,请转到“文件”>“打开”>“项目”并选择 .sln 文件
+
+#ifndef PCH_H
+#define PCH_H
+
+// TODO: 添加要在此处预编译的标头
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+
+#include <MT5APIManager.h>
+#include "ManagerExtension.h"
+
+#endif //PCH_H

+ 11 - 1
MT5MonkPAMM.sln

@@ -3,7 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00
 # Visual Studio 15
 VisualStudioVersion = 15.0.28307.705
 MinimumVisualStudioVersion = 10.0.40219.1
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MT5MonkPAMM", "MT5MonkPAMM\MT5MonkPAMM.vcxproj", "{6380375F-A6B3-496A-84F4-C89BF6315C75}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MT5MonkMAMPlugin", "MT5MonkPAMM\MT5MonkPAMM.vcxproj", "{6380375F-A6B3-496A-84F4-C89BF6315C75}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MT5MonkMAM", "MT5MonkMAM\MT5MonkMAM.vcxproj", "{BB2691DA-EC5C-4CDC-A1D1-8759B1182871}"
 EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -21,6 +23,14 @@ Global
 		{6380375F-A6B3-496A-84F4-C89BF6315C75}.Release|x64.Build.0 = Release|x64
 		{6380375F-A6B3-496A-84F4-C89BF6315C75}.Release|x86.ActiveCfg = Release|Win32
 		{6380375F-A6B3-496A-84F4-C89BF6315C75}.Release|x86.Build.0 = Release|Win32
+		{BB2691DA-EC5C-4CDC-A1D1-8759B1182871}.Debug|x64.ActiveCfg = Debug|x64
+		{BB2691DA-EC5C-4CDC-A1D1-8759B1182871}.Debug|x64.Build.0 = Debug|x64
+		{BB2691DA-EC5C-4CDC-A1D1-8759B1182871}.Debug|x86.ActiveCfg = Debug|Win32
+		{BB2691DA-EC5C-4CDC-A1D1-8759B1182871}.Debug|x86.Build.0 = Debug|Win32
+		{BB2691DA-EC5C-4CDC-A1D1-8759B1182871}.Release|x64.ActiveCfg = Release|x64
+		{BB2691DA-EC5C-4CDC-A1D1-8759B1182871}.Release|x64.Build.0 = Release|x64
+		{BB2691DA-EC5C-4CDC-A1D1-8759B1182871}.Release|x86.ActiveCfg = Release|Win32
+		{BB2691DA-EC5C-4CDC-A1D1-8759B1182871}.Release|x86.Build.0 = Release|Win32
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE

+ 1 - 1
MT5MonkPAMM/MT5MonkPAMM.vcxproj

@@ -24,7 +24,7 @@
     <Keyword>Win32Proj</Keyword>
     <RootNamespace>MT5MonkPAMM</RootNamespace>
     <WindowsTargetPlatformVersion>10.0.17763.0</WindowsTargetPlatformVersion>
-    <ProjectName>MT5MonkMAM</ProjectName>
+    <ProjectName>MT5MonkMAMPlugin</ProjectName>
   </PropertyGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">

+ 183 - 150
MT5MonkPAMM/PluginInstance.cpp

@@ -42,9 +42,14 @@ MTAPIRES CPluginInstance::Start(IMTServerAPI * server)
 		m_api->LoggerOut(MTLogAtt, L"Order subscribe failed [%d]", ret);
 		return ret;
 	}
-	if (ret = m_api->DealSubscribe(this) != MT_RET_OK)
+	//if (ret = m_api->DealSubscribe(this) != MT_RET_OK)
+	//{
+	//	m_api->LoggerOut(MTLogAtt, L"Deal subscribe failed [%d]", ret);
+	//	return ret;
+	//}
+	if (ret = m_api->TradeSubscribe(this) != MT_RET_OK)
 	{
-		m_api->LoggerOut(MTLogAtt, L"Deal subscribe failed [%d]", ret);
+		m_api->LoggerOut(MTLogAtt, L"Trade subscribe failed [%d]", ret);
 		return ret;
 	}
 
@@ -75,13 +80,16 @@ MTAPIRES CPluginInstance::Stop()
 	}
 
 	if ((ret = m_api->PluginUnsubscribe(this)) != MT_RET_OK && ret != MT_RET_ERR_NOTFOUND)
-		m_api->LoggerOut(MTLogErr, L"failed to unsubscribe from plugin config updates [%s (%u)]",
+		m_api->LoggerOut(MTLogErr, L"Failed to unsubscribe from plugin config updates [%s (%u)]",
 			SMTFormat::FormatError(ret), ret);
 	if ((ret = m_api->OrderUnsubscribe(this)) != MT_RET_OK && ret != MT_RET_ERR_NOTFOUND)
-		m_api->LoggerOut(MTLogErr, L"failed to unsubscribe order [%s (%u)]",
+		m_api->LoggerOut(MTLogErr, L"Failed to unsubscribe order sink [%s (%u)]",
 			SMTFormat::FormatError(ret), ret);
-	if ((ret = m_api->DealUnsubscribe(this)) != MT_RET_OK && ret != MT_RET_ERR_NOTFOUND)
-		m_api->LoggerOut(MTLogErr, L"failed to unsubscribe deal [%s (%u)]",
+	//if ((ret = m_api->DealUnsubscribe(this)) != MT_RET_OK && ret != MT_RET_ERR_NOTFOUND)
+	//	m_api->LoggerOut(MTLogErr, L"failed to unsubscribe deal [%s (%u)]",
+	//		SMTFormat::FormatError(ret), ret);
+	if ((ret = m_api->TradeUnsubscribe(this)) != MT_RET_OK && ret != MT_RET_ERR_NOTFOUND)
+		m_api->LoggerOut(MTLogErr, L"Failed to unsubscribe trade sink [%s (%u)]",
 			SMTFormat::FormatError(ret), ret);
 
 	m_api = nullptr;
@@ -148,6 +156,8 @@ void CPluginInstance::OnOrderDelete(const IMTOrder * order)
 	
 	char order_buf[128];
 	sprintf(order_buf, "%lld", order->PositionID());
+	UINT64 position = order->PositionID();
+	UINT64 order_id = order->Order();
 	IMTAccount* account = m_api->UserCreateAccount();
 	IMTOrder* new_order = m_api->OrderCreate();
 
@@ -165,7 +175,8 @@ void CPluginInstance::OnOrderDelete(const IMTOrder * order)
 	if (order->Order() == order->PositionID())
 	{
 		// 如果是新建订单,那么写入新纪录
-		char login_buf[128];
+		char login_buf[128] = { 0 };
+		char req_buf[128] = { 0 };
 		for (auto login : m_followers)
 		{
 			if (m_api->UserAccountGet(login, account) != MT_RET_OK)
@@ -184,6 +195,18 @@ void CPluginInstance::OnOrderDelete(const IMTOrder * order)
 
 			m_api->LoggerOut(MTLogOK, L"add order, vol_init: %lld, vol_cur: %lld", init_volume, volume);
 
+			char msg[1024] = { 0 };
+			sprintf(msg, "orig_position=&lld&login=%lld&source_login=1005&symbol=%s&action=200&type=%d&volume=%lld&price_order=%lf",
+				order->PositionID(), login, ws2s(order->Symbol()).c_str(), order->Type(), init_volume, order->PriceOrder());
+			m_redis_client->publish("dealer_send", msg, [this, position, login, order_id](cpp_redis::reply& r)
+			{
+				// 记录下login order等信息
+				m_api->LoggerOut(MTLogErr, L"original position #%lld, original order #%lld, login %lld, failed to excute dealer_send",
+					);
+			});
+
+			// dealer send之后需要另外一边记录request id
+
 			// 尚未完成建仓,目前position id不填,仅完成订单后记录
 			new_order->Clear();
 			new_order->VolumeInitial(init_volume);
@@ -404,151 +427,161 @@ void CPluginInstance::OnOrderDelete(const IMTOrder * order)
 //	m_api->LoggerOut(MTLogOK, L"OndealClean, Login: %d", login);
 //}
 
-void CPluginInstance::OnDealPerform(const IMTDeal * deal, IMTAccount * account, IMTPosition * position)
-{
-	// position为nullptr时,说明该deal不是由交易本身触发
-	// account是deal完成后的用户状况
-	// position是交易完成后的持仓状况
-	// 对于deal是关闭一个持仓的情况,该position的volume会是0
-	if (position == nullptr
-		|| deal == nullptr
-		|| account == nullptr)
-		return;
-
-	std::lock_guard<decltype(m_lock)> lk(m_lock);
-	if (!m_enable)	return;
-	if (deal->Login() != m_trader)	return;
-
-	//m_api->LoggerOut(MTLogOK, L"OnDealPerform, login: %lld, deal: %lld, ord: %lld, pos: %lld, vol: %lld, volc: %lld",
-	//	deal->Login(), deal->Deal(), deal->Order(), deal->PositionID(), deal->Volume(), deal->VolumeClosed());
-
-	// FIXME: 需要检查现在的deal是否和之前的order对应。如果没有记录到,则没法根本无法正常跟单以及平仓
-	char order_buf[128];
-	sprintf(order_buf, "%lld", deal->PositionID());
-	IMTDeal* new_deal = m_api->DealCreate();
-
-	std::vector<std::string>* fields = nullptr;
-
-	ScopeGuard guard([new_deal, &fields, this]
-	{
-		new_deal->Release();
-		if (fields)
-		{
-			fields->clear();
-			delete fields;
-			fields = nullptr;
-		}
-
-		// 退出前调用commit
-		m_redis_client->commit();
-	});
-
-	char login_buf[128];
-	for (auto login : m_followers)
-	{
-		ScopeGuard g([position, &fields, this, login, login_buf]()
-		{
-			if (position->Volume() == 0)
-			{
-				if (fields == nullptr)
-					fields = new(std::vector<std::string>);
-
-				m_api->LoggerOut(MTLogOK, L"add field %lld to be delete", login);
-				fields->push_back(login_buf);
-			}
-		});
-
-		sprintf(login_buf, "%lld", login);
-		auto fut = m_redis_client->hget(order_buf, login_buf);
-		m_redis_client->sync_commit();
-		auto reply = fut.get();
-
-		m_api->LoggerOut(MTLogOK, L"%lld add deal, original deal #%lld", login, deal->Deal());
-
-		// 如果不存在,忽略
-		if (reply.ko())	continue;
-		if (reply.is_null())	continue;
-
-		// 获取context
-		position_context context;
-		memcpy(&context, reply.as_string().c_str(), sizeof(position_context));
-
-		// 按建仓时的叙述,如果level或者position为0,亦忽略该记录
-		if (context.level == 0)			continue;
-		if (context.position_id == 0)	continue;
-
-		uint64_t volume = context.level * deal->Volume() / 100;
-		uint64_t volume_ext = context.level * deal->VolumeExt() / 100;
-		uint64_t volume_closed = context.level * deal->VolumeClosed() / 100;
-		uint64_t volume_closed_ext = context.level * deal->VolumeClosed() / 100;
-
-		double prop = (double)volume / deal->Volume();
-		double profit = deal->Profit() * prop;
-		double commission = deal->Commission() * prop;
-		double storage = deal->Storage() * prop;
-		double raw_profit = deal->ProfitRaw() * prop;
-
-		m_api->LoggerOut(MTLogOK, L"add new deal, volume %lld, volume closed %lld, order #%lld, position #%lld", volume, volume_closed, context.cur_ord, context.position_id);
-
-		new_deal->Clear();
-		new_deal->Assign(deal);
-
-		new_deal->Login(login);
-		new_deal->DealSet(0);
-		new_deal->Volume(volume);
-		new_deal->VolumeExt(volume_ext);
-		new_deal->VolumeClosed(volume_closed);
-		new_deal->VolumeClosedExt(volume_closed_ext);
-		new_deal->ProfitRaw(raw_profit);
-		new_deal->Profit(profit);
-		new_deal->Commission(commission);
-		new_deal->Storage(storage);
-		new_deal->Order(context.cur_ord);
-		new_deal->PositionID(context.position_id);
-
-		MTAPIRES ret = m_api->DealAdd(new_deal);
-		if (ret != MT_RET_OK)
-		{
-			// TODO: 有没有更多的错误处理?
-			m_api->LoggerOut(MTLogErr, L"%lld cannot add deal [%d] to order #%lld, original deal: #%lld", login, ret, context.cur_ord, deal->Deal());
-			continue;
-		}
-
-		m_api->LoggerOut(MTLogOK, L"add deal #%lld, original deal #%lld", new_deal->Deal(), deal->Deal());
-
-		m_redis_client->publish("mt5_balance_fix", login_buf, [this](cpp_redis::reply r)
-		{
-			if (r.ko())
-			{
-				m_api->LoggerOut(MTLogErr, L"redis publish: %s", r.error().c_str());
-			}
-		});
-		// commit 在退出前完成
-	}
+//void CPluginInstance::OnDealPerform(const IMTDeal * deal, IMTAccount * account, IMTPosition * position)
+//{
+//	// position为nullptr时,说明该deal不是由交易本身触发
+//	// account是deal完成后的用户状况
+//	// position是交易完成后的持仓状况
+//	// 对于deal是关闭一个持仓的情况,该position的volume会是0
+//	if (position == nullptr
+//		|| deal == nullptr
+//		|| account == nullptr)
+//		return;
+//
+//	std::lock_guard<decltype(m_lock)> lk(m_lock);
+//	if (!m_enable)	return;
+//	if (deal->Login() != m_trader)	return;
+//
+//	//m_api->LoggerOut(MTLogOK, L"OnDealPerform, login: %lld, deal: %lld, ord: %lld, pos: %lld, vol: %lld, volc: %lld",
+//	//	deal->Login(), deal->Deal(), deal->Order(), deal->PositionID(), deal->Volume(), deal->VolumeClosed());
+//
+//	// FIXME: 需要检查现在的deal是否和之前的order对应。如果没有记录到,则没法根本无法正常跟单以及平仓
+//	char order_buf[128];
+//	sprintf(order_buf, "%lld", deal->PositionID());
+//	IMTDeal* new_deal = m_api->DealCreate();
+//
+//	std::vector<std::string>* fields = nullptr;
+//
+//	ScopeGuard guard([new_deal, &fields, this]
+//	{
+//		new_deal->Release();
+//		if (fields)
+//		{
+//			fields->clear();
+//			delete fields;
+//			fields = nullptr;
+//		}
+//
+//		// 退出前调用commit
+//		m_redis_client->commit();
+//	});
+//
+//	char login_buf[128];
+//	for (auto login : m_followers)
+//	{
+//		ScopeGuard g([position, &fields, this, login, login_buf]()
+//		{
+//			if (position->Volume() == 0)
+//			{
+//				if (fields == nullptr)
+//					fields = new(std::vector<std::string>);
+//
+//				m_api->LoggerOut(MTLogOK, L"add field %lld to be delete", login);
+//				fields->push_back(login_buf);
+//			}
+//		});
+//
+//		sprintf(login_buf, "%lld", login);
+//		auto fut = m_redis_client->hget(order_buf, login_buf);
+//		m_redis_client->sync_commit();
+//		auto reply = fut.get();
+//
+//		m_api->LoggerOut(MTLogOK, L"%lld add deal, original deal #%lld", login, deal->Deal());
+//
+//		// 如果不存在,忽略
+//		if (reply.ko())	continue;
+//		if (reply.is_null())	continue;
+//
+//		// 获取context
+//		position_context context;
+//		memcpy(&context, reply.as_string().c_str(), sizeof(position_context));
+//
+//		// 按建仓时的叙述,如果level或者position为0,亦忽略该记录
+//		if (context.level == 0)			continue;
+//		if (context.position_id == 0)	continue;
+//
+//		uint64_t volume = context.level * deal->Volume() / 100;
+//		uint64_t volume_ext = context.level * deal->VolumeExt() / 100;
+//		uint64_t volume_closed = context.level * deal->VolumeClosed() / 100;
+//		uint64_t volume_closed_ext = context.level * deal->VolumeClosed() / 100;
+//
+//		double prop = (double)volume / deal->Volume();
+//		double profit = deal->Profit() * prop;
+//		double commission = deal->Commission() * prop;
+//		double storage = deal->Storage() * prop;
+//		double raw_profit = deal->ProfitRaw() * prop;
+//
+//		m_api->LoggerOut(MTLogOK, L"add new deal, volume %lld, volume closed %lld, order #%lld, position #%lld", volume, volume_closed, context.cur_ord, context.position_id);
+//
+//		new_deal->Clear();
+//		new_deal->Assign(deal);
+//
+//		new_deal->Login(login);
+//		new_deal->DealSet(0);
+//		new_deal->Volume(volume);
+//		new_deal->VolumeExt(volume_ext);
+//		new_deal->VolumeClosed(volume_closed);
+//		new_deal->VolumeClosedExt(volume_closed_ext);
+//		new_deal->ProfitRaw(raw_profit);
+//		new_deal->Profit(profit);
+//		new_deal->Commission(commission);
+//		new_deal->Storage(storage);
+//		new_deal->Order(context.cur_ord);
+//		new_deal->PositionID(context.position_id);
+//
+//		MTAPIRES ret = m_api->DealAdd(new_deal);
+//		if (ret != MT_RET_OK)
+//		{
+//			// TODO: 有没有更多的错误处理?
+//			m_api->LoggerOut(MTLogErr, L"%lld cannot add deal [%d] to order #%lld, original deal: #%lld", login, ret, context.cur_ord, deal->Deal());
+//			continue;
+//		}
+//
+//		m_api->LoggerOut(MTLogOK, L"add deal #%lld, original deal #%lld", new_deal->Deal(), deal->Deal());
+//
+//		m_redis_client->publish("mt5_balance_fix", login_buf, [this](cpp_redis::reply r)
+//		{
+//			if (r.ko())
+//			{
+//				m_api->LoggerOut(MTLogErr, L"redis publish: %s", r.error().c_str());
+//			}
+//		});
+//		// commit 在退出前完成
+//	}
+//
+//	m_api->LoggerOut(MTLogOK, L"deal #%lld, position #%lld, volume %lld", deal->Deal(), position->Position(), position->Volume());
+//	if (position->Volume() == 0)
+//	{
+//		// 如果平仓,则删除hash值
+//		if (position->Volume() == 0 && fields != nullptr)
+//		{
+//			m_redis_client->hdel(order_buf, *fields, [this](cpp_redis::reply& r)
+//			{
+//				if (r.ko())
+//				{
+//					// TODO 错误处理
+//					try
+//					{
+//						m_api->LoggerOut(MTLogErr, L"redis: %s", r.error().c_str());
+//					}
+//					catch (...)
+//					{
+//					}
+//				}
+//			});
+//		}
+//
+//		// TODO 当position中的volume为0时,持仓被彻底平调,被跟订单是否也该检查
+//	}
+//}
 
-	m_api->LoggerOut(MTLogOK, L"deal #%lld, position #%lld, volume %lld", deal->Deal(), position->Position(), position->Volume());
-	if (position->Volume() == 0)
+void CPluginInstance::OnTradeRequestAdd(const IMTRequest * request, const IMTConGroup * group, const IMTConSymbol * symbol, const IMTPosition * position, const IMTOrder * order)
+{
+	if (request == nullptr
+		|| group == nullptr
+		|| symbol == nullptr)
 	{
-		// 如果平仓,则删除hash值
-		if (position->Volume() == 0 && fields != nullptr)
-		{
-			m_redis_client->hdel(order_buf, *fields, [this](cpp_redis::reply& r)
-			{
-				if (r.ko())
-				{
-					// TODO 错误处理
-					try
-					{
-						m_api->LoggerOut(MTLogErr, L"redis: %s", r.error().c_str());
-					}
-					catch (...)
-					{
-					}
-				}
-			});
-		}
-
-		// TODO 当position中的volume为0时,持仓被彻底平调,被跟订单是否也该检查
+		return;
 	}
 }
 

+ 7 - 4
MT5MonkPAMM/PluginInstance.h

@@ -17,17 +17,18 @@
 
 struct position_context
 {
-	UINT32	level;
+	UINT64	level;
 	UINT64	position_id;
+	UINT64	request_id;
 	UINT64	cur_ord;
-	INT64	volume; // 正表示buy,负表示sell
+	UINT64	volume; // 正表示buy,负表示sell
 };
 
 class CPluginInstance
 	: public IMTServerPlugin
 	, public IMTConPluginSink
 	, public IMTOrderSink
-	, public IMTDealSink
+	, public IMTTradeSink
 {
 public:
 	CPluginInstance();
@@ -48,7 +49,9 @@ public:
 	//virtual void OnDealUpdate(const IMTDeal* deal);
 	//virtual void OnDealDelete(const IMTDeal* deal);
 	//virtual void OnDealClean(const UINT64 login);
-	virtual void OnDealPerform(const IMTDeal* deal, IMTAccount* account, IMTPosition* position);
+	//virtual void OnDealPerform(const IMTDeal* deal, IMTAccount* account, IMTPosition* position);
+
+	virtual void OnTradeRequestAdd(const IMTRequest* request, const IMTConGroup* group, const IMTConSymbol* symbol, const IMTPosition* position, const IMTOrder* order);
 
 protected:
 	virtual MTAPIRES LoadParam();