Pegar dados DDE do ProfitChart na mesma máquina

Tire suas dúvidas sobre programação em AutoHotkey

Moderator: Gio

luetkmeyer
Posts: 6
Joined: 04 Oct 2019, 09:17

Pegar dados DDE do ProfitChart na mesma máquina

04 Oct 2019, 10:31

Olá pessoal, tudo bem?

Estou escrevendo um programa pra me ajudar a enviar ordens de compra e venda de ações na B3. Situação atual: escrevo dados de compra ou venda em um arquivo no Google Drive (de qualquer máquina) e o AHK lê esse arquivo e executa ordens de compra e venda sobre o programa ProfitChart, em um computador que fica ligado direto em casa.

Preciso implementar um Trailing Stop, e para isso, pretendo usar o DDE do ProfitChart diretamente no AHK. O DDE do Profit abre normalmente no excel (na mesma máquina) e atualiza certinho. Preciso que o AHK pegue esses dados diretamente do Profitchart (AHK e Profitchart estão na mesma máquina).

O valor da célula on excel tem esse padrão "=profitchart|cot!RLOG3.MAX" ou "=profitchart|cot!RLOG3.MIN" etc. (sem aspas), e essa célula possui um valor (10.54 por exemplo).

É possível o AHK receber ou pegar esses dados diretamente do profitchart?
User avatar
Gio
Posts: 1247
Joined: 30 Sep 2013, 10:54
Location: Brazil

Re: Pegar dados DDE do ProfitChart na mesma máquina

04 Oct 2019, 12:21

Bom dia Luetkmeyer.

Seja bem-vindo ao fórum da comunidade do AutoHotkey.

Encontrei uma biblioteca de funções DDE do usuário Majkinetor. Talvez você possa usá-la para conexão ao Profitchart.

Code: Select all

; Copyright 2019 Majkinetor

;Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

;1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

;2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

;3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

;THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

/*
DDE_Callback(nType, nFormat, hConv, hString1, hString2, hData, nData1, nData2)
{
}
*/

DDE_Initialize(idInst = 0, pCallback = 0, nFlags = 0)
{
	If	DllCall("DdeInitialize", "UintP", idInst, "Uint", pCallback, "Uint", nFlags, "Uint", 0)=0
	Return	idInst
}

DDE_Uninitialize(idInst)
{
	Return	DllCall("DdeUninitialize", "Uint", idInst)
}

DDE_GetLastError(idInst)
{
	Return	DllCall("DdeGetLastError", "Uint", idInst)
}

DDE_NameService(idInst, sServ = "", nCmd = 1)
{
	Return	DllCall("DdeNameService", "Uint", idInst, "Uint", hServ:=sServ="" ? 0 : DDE_CreateStringHandle(idInst,sServ), "Uint", 0, "Uint", nCmd), hServ ? DDE_FreeStringHandle(idInst,hServ) : ""
}

DDE_EnableCallback(idInst, hConv = 0, nCmd = 0)
{
	Return	DllCall("DdeEnableCallback", "Uint", idInst, "Uint", hConv, "Uint", nCmd)
}

DDE_PostAdvise(idInst, sTopic = "", sItem = "")
{
	Return	DllCall("DdePostAdvise", "Uint", idInst, "Uint", hTopic:=sTopic="" ? 0 : DDE_CreateStringHandle(idInst,sTopic), "Uint", hItem:=sItem="" ? 0 : DDE_CreateStringHandle(idInst,sItem))
	,	hTopic ? DDE_FreeStringHandle(idInst,hTopic) : ""
	,	hItem  ? DDE_FreeStringHandle(idInst,hItem)  : ""
}

DDE_ClientTransaction(idInst, hConv, sType = "EXECUTE", sItem = "", pData = 0, cbData = 0, CF_Format = 1, bAsync = False){
	static  XTYP_ADVSTART := 0x1030,  XTYP_ADVSTOP := 0x8040, XTYP_EXECUTE := 0x4050, XTYP_POKE	:= 0x4090, XTYP_REQUEST	:= 0x20B0
	Return	DllCall("DdeClientTransaction", "Uint", pData, "Uint", cbData, "Uint", hConv, "Uint", hItem:=sItem="" ? 0 : DDE_CreateStringHandle(idInst,sItem), "Uint", CF_Format, "Uint", XTYP_%sType%, "Uint", bAsync ? TIMEOUT_ASYNC:=-1 : 10000, "UintP", nResult), hItem ? DDE_FreeStringHandle(idInst,hItem) : ""
}

DDE_AbandonTransaction(idInst, hConv = 0, idTransaction = 0)
{
	Return	DllCall("DdeAbandonTransaction", "Uint", idInst, "Uint", hConv, "Uint", idTransaction)
}

DDE_Connect(idInst, sServ = "", sTopic = "", pCC = 0)
{
	Return	DllCall("DdeConnect", "Uint", idInst, "Uint", hServ:=sServ="" ? 0 : DDE_CreateStringHandle(idInst,sServ), "Uint", hTopic:=sTopic="" ? 0 : DDE_CreateStringHandle(idInst,sTopic), "Uint", pCC)
	,	hTopic ? DDE_FreeStringHandle(idInst,hTopic) : ""
	,	hServ  ? DDE_FreeStringHandle(idInst,hServ)  : ""
}

DDE_Reconnect(hConv)
{
	Return	DllCall("DdeReconnect", "Uint", hConv)
}

DDE_Disconnect(hConv)
{
	Return	DllCall("DdeDisconnect", "Uint", hConv)
}

DDE_ConnectList(idInst, sServ = "", sTopic = "", hConvList = 0, pCC = 0)
{
	Return	DllCall("DdeConnectList", "Uint", idInst, "Uint", hServ:=sServ="" ? 0 : DDE_CreateStringHandle(idInst,sServ), "Uint", hTopic:=sTopic="" ? 0 : DDE_CreateStringHandle(idInst,sTopic), "Uint", hConvList, "Uint", pCC)
	,	hTopic ? DDE_FreeStringHandle(idInst,hTopic) : ""
	,	hServ  ? DDE_FreeStringHandle(idInst,hServ)  : ""
}

DDE_DisconnectList(hConvList)
{
	Return	DllCall("DdeDisconnectList", "Uint", hConvList)
}

DDE_QueryNextServer(hConvList, hConvPrev = 0)
{
	Return	DllCall("DdeQueryNextServer", "Uint", hConvList, "Uint", hConvPrev)
}

DDE_QueryConvInfo(hConv, idTransaction = -1, ByRef ci = "")	; QID_SYNC=-1
{
	Return	DllCall("DdeQueryConvInfo", "Uint", hConv, "Uint", idTransaction, "Uint", NumPut(VarSetCapacity(ci,64,0),ci)-4)
}

DDE_AccessData(hData, ByRef cbData = "")
{
	Return	DllCall("DdeAccessData", "Uint", hData, "UintP", cbData)
}

DDE_UnaccessData(hData)
{
	Return	DllCall("DdeUnaccessData", "Uint", hData)
}

DDE_AddData(hData, pData, cbData, cbOff = 0)
{
	Return	DllCall("DdeAddData", "Uint", hData, "Uint", pData, "Uint", cbData, "Uint", cbOff)
}

DDE_GetData(hData, ByRef sData = "", cbOff = 0)
{
	cb  :=	DllCall("DdeGetData", "Uint", hData, "Uint", 0, "Uint", 0, "Uint", cbOff)
	VarSetCapacity(sData, cb)
	If	DllCall("DdeGetData", "Uint", hData, "str", sData, "Uint", cb, "Uint", cbOff)
	Return	sData
}

DDE_QueryString(idInst, hString, nCodePage = 1004)	; CP_WINANSI = 1004, CP_WINUNICODE = 1200
{
	cch :=	DllCall("DdeQueryString", "Uint", idInst, "Uint", hString, "Uint", 0, "Uint", 0, "int", nCodePage)
	VarSetCapacity(sString, cch)
	If	DllCall("DdeQueryString", "Uint", idInst, "Uint", hString, "str", sString, "Uint", cch+1, "int", nCodePage)
	Return	sString
}

DDE_CreateDataHandle(idInst, sItem = "", pData = 0, cbData = 0, cbOff = 0, CF_Format = 1, bOwned = True)
{
	Return	DllCall("DdeCreateDataHandle", "Uint", idInst, "Uint", pData, "Uint", cbData, "Uint", cbOff, "Uint", hItem:=sItem="" ? 0 : DDE_CreateStringHandle(idInst,sItem), "Uint", CF_Format, "Uint", bOwned ? HDATA_APPOWNED:=1 : 0), hItem ? DDE_FreeStringHandle(idInst,hItem) : ""
}

DDE_FreeDataHandle(hData)
{
	Return	DllCall("DdeFreeDataHandle", "Uint", hData)
}

DDE_CreateStringHandle(idInst, sString, nCodePage = 1004)
{
	Return	DllCall("DdeCreateStringHandle", "Uint", idInst, "Uint", &sString, "int", nCodePage)
}

DDE_KeepStringHandle(idInst, hString)
{
	If	DllCall("DdeKeepStringHandle", "Uint", idInst, "UintP", hString)
	Return	hString
}

DDE_FreeStringHandle(idInst, hString)
{
	Return	DllCall("DdeFreeStringHandle", "Uint", idInst, "Uint", hString)
}

DDE_CmpStringHandles(hString1, hString2)
{
	Return	DllCall("DdeCmpStringHandles", "Uint", hString1, "Uint", hString2)
}

DDE_SetUserHandle(hConv, hUser)
{
	Return	DllCall("DdeSetUserHandle", "Uint", hConv, "Uint", -1, "Uint", hUser)
}
Link do Projeto: https://code.google.com/archive/p/multi-rename-script/
Guest

Re: Pegar dados DDE do ProfitChart na mesma máquina

05 Oct 2019, 10:04

Bom dia.

Infelizmente não consegui fazer funcionar. Não entendo o suficiente de funções pra fazer isso.

Você conhece alguma comunidade que tem desenvolvedor AHK pra fazer esse serviço pago?

Novamente agradeço.
luetkmeyer
Posts: 6
Joined: 04 Oct 2019, 09:17

Re: Pegar dados DDE do ProfitChart na mesma máquina

10 Oct 2019, 16:50

Continuo tentando, ainda sem sucesso.

Você conseguiria verificar o link da referência que me enviou? Preciso encontrar um pouco mais de informações, aprender a usar essa função. Estou precisando muito.

Aproveitando, conhece alguém que conseguiria me ajudar, mesmo que eu pague?

Novamente agradeço.
Guest

Re: Pegar dados DDE do ProfitChart na mesma máquina

10 Oct 2019, 20:08

Tentei esse: https://autohotkey.com/board/topic/23467-dde-server-for-single-instance-file-association-std-lib/
Com esse código:

Code: Select all

; Initialize the DDE server.
result := DDE_InitServer("profitchart", "cot", "BTOW3.MAX")
MsgBox % result
; Let the user know if command-line args were specified.
n = 1
if 0 > 0 ; var '0' contains the number of args
    MsgBox % "args: " %n% ; access arg '1' indirectly via 'n'

return

; When you double-click a file (or select the appropriate action from the
; context menu), assuming the file type is associated correctly the script will
; call this sub-routine.
DDE_Command:
    ; For now just show what command was received.
    MsgBox %DDE_Cmd%
return


Tentei esse: https://autohotkey.com/board/topic/20174-ahk-dde-functions-including-asyncbatchmulti-channel/
Com esse código:

Code: Select all

connect :=	DDE_Connect("profitchart","cot") 
try1 :=	DDE_Execute("BTOW3")
try2 :=	DDE_Execute("BTOW3.MAX")
try3 :=	DDE_Execute(BTOW3.MAX)
;try4 :=	DDE_Execute("=profitchart|cot!BTOW3.MAX")
;try2 :=	DDE_Request("BTOW3.MAX")
try5 :=	DDE_Poke("BTOW3.MAX", "cot")
try6 :=	DDE_Poke("BTOW3", "MAX")
;DDE_Poke("[COMMAND]", "[DATASET FOR COMMAND]",
try=
Loop, 6
 try .= "`n" try%a_index%
msgbox connect = %connect%`n`n%try%`n
msgbox 1%try1%`n2%try2%`n3%try3%`n4%try4%`n5%try5%`n6%try6%
; profitchart|cot!BTOW3.MAX



msgbox % DDE_Request("BTOW3.MAX") "`n" DDE_Request("[BTOW3.MAX]")
msgbox % DDE_Poke("[BTOW3.MAX]", "[cot]")
msgbox % DDE_Poke("[BTOW3]", "[MAX]")
msgbox % DDE_Execute("[""BTOW3.MAX""]")
DDE_KILL()
ExitApp
Nenhuma variação funcionou, a resposta é sempre o mesmo número 199532

E esse:

Code: Select all


idInst := DDE_Initialize(0, RegisterCallback("DDE_Callback")) 
hConv := DDE_Connect(idInst,"profitchart","cot") 
tick := DDE_ClientTransaction(idInst, hConv, "EXECUTE")
; not sure where "BID!GBPUSD" request goes, maybe in DDE_AccessData()?
w := DDE_AccessData("BTOW3.MAX")		
DDE_Uninitialize(idInst) 
msgbox idInst = %idInst%`nhConv = %hConv%`n%tick%`n`nw = %w%

com o DDE lib by Majkinetor, indicado aqui.

Não consegui com nenhum deles.

Alguma ideia?
User avatar
Gio
Posts: 1247
Joined: 30 Sep 2013, 10:54
Location: Brazil

Re: Pegar dados DDE do ProfitChart na mesma máquina

11 Oct 2019, 10:24

Bom dia Luetkmeyer.

Andei pesquisando um pouco mais sobre a tecnologia DDE. É uma tecnologia antiga, precursora até mesmo do modelo COM. É considerada em desuso, mas a boa notícia é que o Windows mantém suporte a ela por questões de compatibilidade com sistemas antigos.

Vejo que as discussões acerca da implementação da tecnologia em AutoHotkey são bem antigas também. Isso traz alguns problemas, pois até 2010 usávamos a versão 1.0 do programa, que tinha uma sintaxe um pouco diferente da versão atual (1.1+) em alguns aspectos. Por isso alguns códigos antigos não funcionam.

Tem um tópico com uma discussão maior sobre o tema neste link. Pelo que entendi, eles parecem estar usando a sintaxe do AutoHokey _L (que mais tarde veio a se tornar o AutoHotkey 1.1+), o que pode ajudar. O problema é que o assunto não é pequeno, e pelo que está escrito nos posts é necessário alterar algumas chaves de registro para que os códigos funcionem (e para completar os códigos foram testados em Windows XP).

Também não tenho acesso nem utilizo o programa ProfitChart (na verdade nem mexo com DayTrade) então nesse quesito fica difícil para mim realizar algum teste.

Mas fiquei pensando, será mesmo que o DDE é a única forma de implementar o TrailingStop que você busca? E porque exatamente você quer remover a conexão via o Excel?
Guest

Re: Pegar dados DDE do ProfitChart na mesma máquina

11 Oct 2019, 13:23

Legal Gio, muito obrigado pela sua resposta.

Vou analisar o link e tentar implementar.

Fiz um script usando COM pra copiar do excel (que atualiza dados via DDE como stream), mas "fica pesado" pra máquina rodar o dia inteiro (o comando COM fica copiando a cada 10 seg e o ahk faz a análise matemática que preciso). Se eu conseguisse pegar o DDE direto com o ahk, ficaria MUITO mais leve (eu poderia até colocar uma frequência de cálculo maior que os 10 segundos). A ideia é fazer trailingstop automático em Swing Trade (quando chegar em 2, 3% de lucro na semana).

Alternativamente, tentei de todas as formas pegar dados da janela ativa (wingettext, winget, controlget, controlget list, etc. etc.), mas não sei o porque, não fica visível para o ahk.

Novamente agradeço a ajuda, irei procurar mais sobre.
User avatar
Gio
Posts: 1247
Joined: 30 Sep 2013, 10:54
Location: Brazil

Re: Pegar dados DDE do ProfitChart na mesma máquina

11 Oct 2019, 15:26

mas "fica pesado" pra máquina rodar o dia inteiro ... Se eu conseguisse ... eu poderia até colocar uma frequência de cálculo maior que os 10 segundos

Já está usando o SetBatchLines, -1 no início do seu script?

Outra questão: como está coletando os dados da planilha?

:arrow: Você pode criar um objeto 2D com os dados de um intervalo inteiro da planilha usando uma seleção. Dessa forma, apenas 1 comando COM é executado a cada vez que precise recuperar os dados da planilha.

Exemplo: criar um objeto 2D da célula A2 até a célula G10 e depois exibir os valores célula por célula:

Code: Select all


; Considerando abaixo que X1 já contém o resultado da chamada ComObjActive() à planilha alvo do Excel.

X1.Range( "A2:G10" ).Select ; Ajuste aqui o intervalo das células relevantes.
OBJETO_TABELA := X1.Selection.Value
Loop % OBJETO_TABELA.MaxIndex()
{
	LINHA_ATUAL := A_Index
	Loop % OBJETO_TABELA[1].MaxIndex()
	{
		msgbox % OBJETO_TABELA[LINHA_ATUAL, A_Index]
	}
}
luetkmeyer
Posts: 6
Joined: 04 Oct 2019, 09:17

Re: Pegar dados DDE do ProfitChart na mesma máquina

15 Oct 2019, 10:13

Olá.

A estratégia que você recomendou funcionou muito bem, melhorou o desempenho e estou usando atualização a cada 10 segundos sem pesar a máquina. Muito obrigado!!!

Return to “Ajuda e Suporte Geral”

Who is online

Users browsing this forum: No registered users and 28 guests