德美利证券.NET

TD Ameritrade .NET

有没有人让 TD Ameritrade 流 API 与 C# 一起工作?他们在 https://developer.tdameritrade.com/content/streaming-data. I have gotten the JavaScript examples to work on https://js.do/, but can't get anything similar to work in .NET. This is a shortened version of what I'm trying to do. I can't include exactly what I'm sending because I'm trying to send the login message which includes account information, but I can say that I copy and pasted the exact JSON message that is working in my JavaScript tests into the file LoginJSON.txt in this example. In this example the socket will just close as soon as I send the message, no text response at all. If however I send an intentionally malformatted message I'll actually get text response saying the message is malformatted and then get a socket disconnect. Their support has been non-responsive which I understand to the the norm. There are some python examples here https://www.youtube.com/channel/UCBsTB02yO0QGwtlfiv5m25Q 处提供了一些文档和 JavaScript 示例,但我已经全部看完了,但没有学到任何东西来帮助我的代码正常工作。

        ClientWebSocket socket = new ClientWebSocket();
        var connectAsync = socket.ConnectAsync(new Uri("wss://streamer-ws.tdameritrade.com/ws"), CancellationToken.None);
        string loginRequest;
        using (StreamReader re = new StreamReader("LoginJSON.txt")) {
            loginRequest = re.ReadToEnd();
        }

        connectAsync.Wait();

        Thread readThread = new Thread(
            delegate(object obj)
            {
                while (true) {
                    if (socket.State == WebSocketState.Open) {
                        Console.Out.WriteLine("Waiting");
                        byte[] recBytes = new byte[1024];
                        var clientBuffer = new ArraySegment<byte>(recBytes);
                        var receiveAsync = socket.ReceiveAsync(clientBuffer, CancellationToken.None);
                        receiveAsync.Wait();
                        switch (receiveAsync.Result.MessageType) {
                            case WebSocketMessageType.Text:
                                var s = Encoding.UTF8.GetString(recBytes);
                                Console.Out.WriteLine(s.Trim());
                                break;
                            case WebSocketMessageType.Close:
                                Console.Out.WriteLine("Close message received");
                                break;
                            default:
                                throw new ArgumentOutOfRangeException();
                        }
                    }
                }
            });

        readThread.Start();
        socket.SendAsync(Encoding.UTF8.GetBytes(loginRequest), WebSocketMessageType.Text, true, CancellationToken.None);
        Console.ReadLine();

我尝试了 WebSocketClient 方法,但从未成功。我得到的错误与您得到的完全相同。确切地。我发现 WebSocketClient 实际上使 javascript 中非常容易实现的事情变得复杂。只需让您的 C# 调用 javascript 函数来执行 javascript 并将响应发送回给您。我已经在 Blazor 应用程序中使用 C# 以这种方式工作,并且它可以无缝工作。

我遇到了同样的问题,我设法解决了。在我的例子中,时间戳准备不正确,需要计算时间戳以获得TokenTimestamp 属性,应该将其转换为世界时。对不起,我的英语来自 google 翻译。 :) 这是正确的代码:

DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
DateTime tokenDate = Convert.ToDateTime(userPrincipal.StreamerInfo.TokenTimestamp);
TimeSpan tokenEpoch = tokenDate.ToUniversalTime() - epoch;
long timestamp = (long)Math.Floor(tokenEpoch.TotalMilliseconds);

var credentials = new Credentials
{
    userid = userPrincipal.Accounts[0].AccountId,
    token = userPrincipal.StreamerInfo.Token,
    company = userPrincipal.Accounts[0].Company,
    segment = userPrincipal.Accounts[0].Segment,
    cddomain = userPrincipal.Accounts[0].AccountCdDomainId,
    usergroup = userPrincipal.StreamerInfo.UserGroup,
    accesslevel = userPrincipal.StreamerInfo.AccessLevel,
    authorized = "Y",
    timestamp = timestamp,
    appid = userPrincipal.StreamerInfo.AppId,
    acl = userPrincipal.StreamerInfo.Acl
};
var credentialArr = credentials.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public).Select(p => new KeyValuePair<string, string>(p.Name, p.GetValue(credentials, null).ToString()));
var loginRequest = new Request
{
    service = "ADMIN",
    command = "LOGIN",
    requestid = "0",
    account = userPrincipal.Accounts[0].AccountId,
    source = userPrincipal.StreamerInfo.AppId,
    parameters = new Parameters
    {
        credential = string.Join("&", credentialArr.Where(c => !string.IsNullOrWhiteSpace(c.Value)).Select(c => string.Format("{0}={1}", HttpUtility.UrlEncode(c.Key, Encoding.UTF8), HttpUtility.UrlEncode(c.Value, Encoding.UTF8)))),
        token = userPrincipal.StreamerInfo.Token,
        version = "1.0",
        qoslevel = "0"
    }
};
var req = JsonConvert.SerializeObject(Requests.ToRequests(loginRequest), Formatting.None, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
socketClient = new WebSocket(string.Format("wss://{0}/ws", userPrincipal.StreamerInfo.StreamerSocketUrl));
if(Environment.OSVersion.Version.Major > 5)
{
    socketClient.SslConfiguration.EnabledSslProtocols = (System.Security.Authentication.SslProtocols)3072;
    socketClient.SslConfiguration.ServerCertificateValidationCallback = (sender, cert, chain, sslPolicyErrors) => { return true; };
}
socketClient.Connect();
socketClient.Send(req);

@米哈伊尔, 你会分享你的代码来获取用户主体吗?这是我的代码,但我得到 status=401 即使我的访问令牌有效(我已经通过 API 页面对其进行了测试):

    using System;
    using WebSocketSharp;
    using System.Net.Http;
    using System.Threading.Tasks;

namespace TdLogin
{
    class Program
    {
        static async Task  Main(string[] args)
        {
            string accessToken = util.accessToken; // get the access token from util 
            Console.WriteLine("Hello World!");
            var client = new HttpClient();
            client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("bearer", accessToken);
            var result = await client.GetAsync("https://api.tdamer`enter code here`itrade.com/v1/userprincipals?fields=streamerSubscriptionKeys%2CstreamerConnectionInfo");
            Console.WriteLine("status= {0}", result.StatusCode);
            Console.WriteLine(result.Content);
            Console.ReadKey();
        }
    }
}

这是 GitHub 上的 C# API。 它们还支持其他语言:C#、Python、JavaScript、C++ 和 Ruby。 https://github.com/td-ameritrade

不知道是不是TD Ameritrade官方的

好吧,TDA 报价总是以(纽约)东部时间为准,因为它是纽约证券交易所的标准。这就是为什么旧手表会显示不止一次的原因。

我还没有真正玩过streaming,但是玩过epoch time。我在东部时间,所以我不必处理转换。因此,以下转换未经实战测试,但以下 C# 方法可能会帮助将来偶然发现此线程的人。

    private static readonly TimeZoneInfo TDAServerTimeZone = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
    public static DateTime ToServerTime(this DateTime dateTime)
    {
        return TimeZoneInfo.ConvertTime(dateTime, TDAServerTimeZone);
    }

    public static DateTime ToLocalTime(this DateTime dateTime)
    {
        TimeZoneInfo LocalTimeZone = TimeZoneInfo.Local;
        return TimeZoneInfo.ConvertTime(dateTime, TDAServerTimeZone, LocalTimeZone);
    }

    public static DateTime ToUTCTime(this DateTime dateTime)
    {
        TimeZoneInfo UTCTimeZone = TimeZoneInfo.Utc;
        return TimeZoneInfo.ConvertTime(dateTime, TDAServerTimeZone, UTCTimeZone);
    }

    public static DateTime FromUnixTime(long unixTime)
    {
        DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0);
        return epoch.AddMilliseconds(unixTime);
    }

    public static long ToUnixTime(DateTime dtConvert)
    {
        TimeSpan t = dtConvert - new DateTime(1970, 1, 1);
        long SinceEpoch = (long)t.TotalMilliseconds;
        return SinceEpoch;
    }