import datetime as dt
import json
import os
from ctrader_open_api import Client, Protobuf, TcpProtocol, EndPoints
import ctrader_open_api.messages.OpenApiMessages_pb2 as OA
import ctrader_open_api.messages.OpenApiModelMessages_pb2 as OAModel
import ctrader_open_api.messages.OpenApiCommonMessages_pb2 as OACommon
import ctrader_open_api.messages.OpenApiCommonModelMessages_pb2 as OAModelCommon
from twisted.internet import reactor
PROTO_OA_ERROR_RES_PAYLOAD_TYPE = OA.ProtoOAErrorRes().payloadType
credentials = json.load(open('credentials.json'))
client = Client(EndPoints.PROTOBUF_DEMO_HOST, EndPoints.PROTOBUF_PORT, TcpProtocol)
def onAccAuth(message):
if message.payloadType == PROTO_OA_ERROR_RES_PAYLOAD_TYPE:
print('account authentication failed', '\n')
print(Protobuf.extract(message), '\n')
return
print('account authenticated')
main()
def onAppAuth(message):
if message.payloadType == PROTO_OA_ERROR_RES_PAYLOAD_TYPE:
print('app authentication failed', '\n')
print(Protobuf.extract(message), '\n')
return
print('app authenticated')
req = OA.ProtoOAAccountAuthReq()
req.ctidTraderAccountId = credentials['accountId']
req.accessToken = credentials['accessToken']
deferred = client.send(req)
deferred.addCallbacks(onAccAuth, onError)
def onError(failure):
print('err: ', repr(failure.value))
def connected(client):
print('connected')
req = OA.ProtoOAApplicationAuthReq()
req.clientId = credentials['clientId']
req.clientSecret = credentials['clientSecret']
deferred = client.send(req, responseTimeoutInSeconds=20)
deferred.addCallbacks(onAppAuth, onError)
def disconnected(client, reason):
print('disconnected: ', reason)
def onMsg(client, message):
ignores = [i.payloadType for i in [OACommon.ProtoHeartbeatEvent(), OA.ProtoOAAccountAuthRes(), OA.ProtoOAApplicationAuthRes()]]
if message.payloadType in ignores:
return
print('message received')
def onTickData(message):
response = Protobuf.extract(message)
if message.payloadType == OA.ProtoOAErrorRes().payloadType:
print('server sent error')
print(response)
return
if response.hasMore:
print('time range specified contains more ticks than allowed per request')
tickData = response.tickData
if not len(tickData):
print('there were no ticks in the date range you specified')
ticks = []
prev_timestamp = tickData[0].timestamp
prev_tick = tickData[0].tick
for i, v in enumerate(tickData):
timestamp = prev_timestamp + v.timestamp if i > 0 else v.timestamp
tick = prev_tick + v.tick if i > 0 else v.tick
tick_normalized = tick / 100_000
ticks.append(map(str, [timestamp, tick_normalized]))
prev_timestamp, prev_tick = timestamp, tick
print(ticks)
def main():
for file in ['ticks.bid.csv', 'ticks.ask.csv']:
if os.path.exists(file): os.remove(file)
sym_id = 41
datetime_range = [(2025,1,2,18,30), (2025,1,2,18,45)]
frm, to = [dt.datetime(*i, tzinfo=dt.UTC).timestamp() for i in datetime_range]
if to - frm > dt.timedelta(weeks=1).total_seconds():
raise ValueError('cannot request tick data for a period larger than one week!')
req = OA.ProtoOAGetTickDataReq()
req.symbolId = sym_id
req.ctidTraderAccountId = credentials['accountId']
req.type = OAModel.ProtoOAQuoteType.BID
req.fromTimestamp = int(frm * 1000)
req.toTimestamp = int(to * 1000)
deferred = client.send(req, responseTimeoutInSeconds=20)
deferred.addCallbacks(onTickData, onError)
client.setConnectedCallback(connected)
client.setDisconnectedCallback(disconnected)
client.setMessageReceivedCallback(onMsg)
client.startService()
reactor.run()