Swift/Python 
            
                 10 Jan 2025, 14:20
            
                    
I'm trying to get Python script to request MarketData into MacOS app , does anyone have some experience how this could be done ?
I have created config.json as instructed , it does work logging me in and do receive message
“received: 8=FIX.4.4|9=135|35=W|34=10|49=cServer|50=QUOTE|52=20250110-14:19:04.102|56=demo.ctrader.xxxxxxx|57=QUOTE|55=1|268=2|269=0|270=1.02628|269=1|270=1.0263|10=044| ”
Any idea which Swift Library or conversion to use to create this process in Xcode IDE ?
 
simple EURUSD.py
from twisted.internet import reactor
import json
from ctrader_fix import *
# Callback for receiving all messages
def onMessageReceived(client, responseMessage):
    print("Received: ", responseMessage.getMessage().replace("", "|"))
    messageType = responseMessage.getFieldValue(35)
    if messageType == "A":
        print("We are logged in")
        requestMarketData(client)  # Request market data after logging in
# Callback for client disconnection
def disconnected(client, reason):
    print("Disconnected, reason: ", reason)
# Callback for client connection
def connected(client):
    print("Connected")
    logonRequest = LogonRequest(config)
    client.send(logonRequest)
# Function to request market data for EUR/USD
def requestMarketData(client):
    marketDataRequest = MarketDataRequest(config)
    
    # Set the required fields for the market data request
    marketDataRequest.MDReqID = "EURUSD_MarketData"  # Unique request ID
    marketDataRequest.SubscriptionRequestType = "1"  # 1 = Snapshot + Updates
    marketDataRequest.MarketDepth = "1"  # Depth of market
    marketDataRequest.NoMDEntryTypes = "1"  # Number of MD entry types
    marketDataRequest.MDEntryType = "0"  # 0 = Bid, 1 = Offer
    marketDataRequest.NoRelatedSym = "1"  # Number of related symbols
    marketDataRequest.Symbol = "1"  # The symbol for which you want market data
    client.send(marketDataRequest)
    print("Market data request sent for EUR/USD")
# Load configuration
with open("config.json") as configFile:
    config = json.load(configFile)
client = Client(config["Host"], config["Port"], ssl=config["SSL"])
# Setting client callbacks
client.setConnectedCallback(connected)
client.setDisconnectedCallback(disconnected)
client.setMessageReceivedCallback(onMessageReceived)
# Starting the client service
client.startService()
reactor.run()
 
Replies
                     algobeginner
                     12 Jan 2025, 23:23
                                            ( Updated at: 12 Jan 2025, 23:43 )
                                    
Update - lot cleaner , need work bodylenght ( 9 )
import Foundation
import Combine
class FIXClientViewModel: ObservableObject {
    @Published var connectionStatus: String = "Disconnected"
    @Published var receivedMessage: String = ""
    
    private var client: FIXClient?
    private var config: Config?
    
    init() {
        loadConfig()
    }
    
    func loadConfig() {
        guard let configURL = Bundle.main.url(forResource: "config", withExtension: "json") else {
            print("Config file not found.")
            return
        }
        
        do {
            let data = try Data(contentsOf: configURL)
            let config = try JSONDecoder().decode(Config.self, from: data)
            self.config = config
            setupClient()
        } catch {
            print("Error loading config: \(error)")
        }
    }
    
    func setupClient() {
        guard let config = config else {
            return
        }
        
        client = FIXClient(host: config.Host, port: config.Port, ssl: config.SSL)
        
        client?.setConnectedCallback { [weak self] in
            self?.connectionStatus = "Connected"
        }
        
        client?.setDisconnectedCallback { [weak self] error in
            self?.connectionStatus = error != nil ? "Disconnected" : "Error"
        }
        
        client?.setMessageReceivedCallback { [weak self] message in
            self?.receivedMessage = message.getMessage()
        }
    }
    
    func connectToServer() {
        client?.startService()
    }
    
    struct MessageField {
        let tag: Int
        let value: String
        
    }
    func sendLogonMessage() {
        guard let config = config else {
            return
        }
        
        let timestamp = getCurrentUTCTimestamp()
        // Define the fields in the correct order for the Logon message (35=A)
        let messageFields: [MessageField] = [
            MessageField(tag: 8, value: config.BeginString),   // 8=FIX.4.4 (BeginString)
            MessageField(tag: 9, value: ""),                   // 9=BodyLength (will be calculated)
            MessageField(tag: 35, value: "A"),                 // 35=A (Logon message type)
            MessageField(tag: 49, value: config.SenderCompID), // 49=SenderCompID (ID of the trading party)
            MessageField(tag: 56, value: config.TargetCompID), // 56=CSERVER (ID of the target component)
            MessageField(tag: 34, value: String(1)),           // 34=1 (MsgSeqNum, sequence number)
            MessageField(tag: 52, value: timestamp),                  // 52=SendingTime (will be set dynamically)
            MessageField(tag: 57, value: "QUOTE"),             // 57=TRADE (TargetSubID)
            MessageField(tag: 50, value: "Quote"),             // 50=Quote (SenderSubID)
            MessageField(tag: 98, value: "0"),                 // 98=0 (EncryptMethod, no encryption)
            MessageField(tag: 108, value: config.HeartBeat),               // 108=30 (Heartbeat interval)
            MessageField(tag: 141, value: "Y"),                // 141=Y (ResetSeqNumFlag)
            MessageField(tag: 553, value: config.Username),    // 553=Username (user ID)
            MessageField(tag: 554, value: config.Password),    // 554=Password (user password)
            MessageField(tag: 10, value: "")                   // 10=Checksum (calculated later)
        ]
        // Create the FIX message object
        var logonMessage = FIXMessage()
        // Add fields to the FIX message in the defined order, excluding 9 and 10 for now
        for field in messageFields {
            logonMessage.setField(field.tag, field.value)
        }
        // Dynamically set the SendingTime (52) to the current UTC timestamp
        let currentTime = getCurrentUTCTimestamp()
        logonMessage.setField(52, currentTime)  // Set SendingTime (52)
        // Now, calculate the message body length (excluding the BodyLength itself, i.e., excluding tag 9)
        let messageString = logonMessage.getMessage()
        // Calculate the message length (excluding the BodyLength field itself)
        let messageLength = messageString.count + 1 // Include the Message Length field itself (9)
        let messageLengthString = String(messageLength)
        // Now, set the '9' field (BodyLength) in the second position
        logonMessage.setField(9, messageLengthString)  // Add BodyLength as second field
        // Calculate checksum (tag 10)
        let checksum = calculateChecksum(for: messageString)
        // Set the '10' field with the calculated checksum as the last field
        logonMessage.setField(10, checksum)  // Add checksum at the end of the message
        
        // Generate the fixed-format message string
        let fixedFormatMessage = logonMessage.getMessage()
        // Debugging: print the logon message in fixed format
        print("Logon Message (Fixed Format): \(fixedFormatMessage)")
        // Send the logon message
        client?.send(logonMessage)
    }
 
            }
    
    // Function to get the current UTC timestamp in the required format (yyyyMMdd-HH:mm:ss)
    func getCurrentUTCTimestamp() -> String {
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "yyyyMMdd-HH:mm:ss"
        dateFormatter.timeZone = TimeZone(abbreviation: "UTC")  // Set the timezone to UTC
        let date = Date()
        return dateFormatter.string(from: date)
        
        
    }
    // Example checksum calculation function (you will need to implement this based on your specific needs)
    func calculateChecksum(for message: String) -> String {
        let checksum = message.reduce(0) { $0 + Int($1.asciiValue ?? 0) }
        return String(checksum % 256)
    }
    
 
@algobeginner

algobeginner
11 Jan 2025, 21:22
Swift code in IDE
import Cocoaimport Foundationimport Network// Configuration for FIX APIstruct FIXConfig {var Host: Stringvar Port: Intvar SSL: Boolvar Username: Stringvar Password: Stringvar BeginString: Stringvar SenderCompID: Stringvar SenderSubID: Stringvar TargetCompID: Stringvar TargetSubID: Stringvar HeartBeat: Int}// Get current timestamp in FIX-compatible formatfunc getCurrentTimestamp() -> String {let dateFormatter = DateFormatter()dateFormatter.dateFormat = "yyyyMMdd-HH:mm:ss"return dateFormatter.string(from: Date())}// Calculate the checksum for the message (FIX requires it for integrity)func generateChecksum(for message: String) -> String {let messageBytes = [UInt8](message.utf8)let checksum = messageBytes.reduce(0, { $0 + Int($1) }) % 256return String(format: "%03d", checksum)}// Function to handle connection and sending logon messagefunc connectToServer(config: FIXConfig) -> NWConnection {let host = NWEndpoint.Host(config.Host)let port = NWEndpoint.Port(integerLiteral: UInt16(config.Port))let connection = NWConnection(host: host, port: port, using: .tcp)connection.stateUpdateHandler = { state inswitch state {case .ready:print("Connection established. Sending Logon message...")sendLogonMessage(connection: connection, config: config)case .failed(let error):print("Connection failed with error: \(error)")default:break}}connection.start(queue: .global())return connection}// Function to send Logon Messagefunc sendLogonMessage(connection: NWConnection, config: FIXConfig) {var logonMessage = """8=\(config.BeginString)\u{1}35=A\u{1}49=\(config.SenderCompID)\u{1}56=\(config.TargetCompID)\u{1}34=1\u{1}52=\(getCurrentTimestamp())\u{1}108=\(config.HeartBeat)\u{1}"""let checksum = generateChecksum(for: logonMessage)logonMessage.append("10=\(checksum)\u{1}")print("Sending logon message: \(logonMessage)") // Log the messagelet data = Data(logonMessage.utf8)connection.send(content: data, completion: .contentProcessed({ error inif let error = error {print("Error sending logon message: \(error)")} else {print("Logon message sent successfully.")requestMarketData(connection: connection, config: config)}}))// Start listening for messages after sending logonlistenForMessages(connection: connection)}// Function to send Market Data Request for EUR/USDfunc requestMarketData(connection: NWConnection, config: FIXConfig) {let mdReqID = "EURUSD_MarketData"let subscriptionRequestType = "1" // Snapshot + Updateslet marketDepth = "1" // Top of booklet noMDEntryTypes = "1" // Number of MD Entry Typeslet mdEntryType = "0" // Bidlet noRelatedSym = "1" // Number of related symbolslet symbol = "1" // EUR/USD symbollet mdUpdateType = "0" // Full refreshvar marketDataRequest = """8=\(config.BeginString)\u{1}35=V\u{1}49=\(config.SenderCompID)\u{1}56=\(config.TargetCompID)\u{1}34=2\u{1}52=\(getCurrentTimestamp())\u{1}262=\(mdReqID)\u{1}263=\(subscriptionRequestType)\u{1}264=\(marketDepth)\u{1}265=\(noMDEntryTypes)\u{1}269=\(mdEntryType)\u{1}146=\(noRelatedSym)\u{1}55=\(symbol)\u{1}265=\(mdUpdateType)\u{1}"""let checksum = generateChecksum(for: marketDataRequest)marketDataRequest.append("10=\(checksum)\u{1}")print("Sending market data request: \(marketDataRequest)") // Log the messagelet data = Data(marketDataRequest.utf8)connection.send(content: data, completion: .contentProcessed({ error inif let error = error {print("Error sending market data request: \(error)")} else {print("Market data request sent successfully.")}}))}// Listen for incoming messages and handle logon acknowledgment or market data responsesfunc listenForMessages(connection: NWConnection) {connection.receive(minimumIncompleteLength: 1, maximumLength: 1024) { (data, context, isComplete, error) inif let data = data {let receivedMessage = String(decoding: data, as: UTF8.self)print("Raw received message: \(receivedMessage)") // Log the raw message// Print raw bytesprint("Raw bytes: \(data.map { String(format: "%02x", $0) }.joined(separator: " "))")// Debug: Check if we're receiving an empty messageif data.isEmpty {print("Received an empty message. Waiting for more data...")}// Handle the different message types (A = Logon, V = Market Data)let messageType = receivedMessage.split(separator: "\u{1}").first { $0.hasPrefix("35=") }?.split(separator: "=").last ?? ""if messageType == "A" {// Logon acknowledgment receivedprint("Logon acknowledgment received!")// Now, you can send the market data request here if you haven't yet} else if messageType == "V" {// Market Data response (Type V)print("Received Market Data Update")processMarketDataUpdate(receivedMessage)} else {print("Received unhandled message: \(receivedMessage)")}}// Handle when the connection is complete or encounters an errorif isComplete {connection.cancel()print("Connection closed.")} else if let error = error {print("Error receiving message: \(error)")connection.cancel()} else {// Continue listening for more messageslistenForMessages(connection: connection)}}}// Example: Process Market Data Updatefunc processMarketDataUpdate(_ message: String) {// Print and process the market data responseprint("Processing Market Data Update: \(message)")// Example: You can extract the bid/ask data from the message.// You will need to adjust parsing according to your FIX message format.let messageParts = message.split(separator: "\u{1}") // Split by the ASCII delimiterfor part in messageParts {print("Part: \(part)") // Print each part of the response message}}// macOS ViewControllerclass ViewController: NSViewController {let fixSessionManager = FIXSessionManager()override func viewDidLoad() {super.viewDidLoad()// Start FIX session after the view is loadedfixSessionManager.startFIXSession()}}config
class FIXSessionManager {func startFIXSession() {let config = FIXConfig(Host: "demo-uk-eqx-02.p.ctrader.com",Port: 5201,SSL: false,Username: "1111111",Password: "password",BeginString: "FIX.4.4",SenderCompID: "demo.ctrader.1111111",SenderSubID: "QUOTE",TargetCompID: "cServer",TargetSubID: "QUOTE",HeartBeat: 30)let connection = connectToServer(config: config)listenForMessages(connection: connection)}}still no response from server , any idea how to get this working , I tried this PythonKit but its so buggy it won't load even after environment creation for Python and Hardened Runtime disabled Library Validation .
Yes I tried, googling , AI , but no solution .
@algobeginner