予想外にはまったのでメモしておきます。
パーサーは Newtonsoft.Json(JSON.NET) を使用します。
Cryptowatch API で取得したデータは以下のような JSON 形式となっています。
{
"result": {
"60": [
[1481634360, 782.14, 782.14, 781.13, 781.13, 1.92525],
[1481634420, 782.02, 782.06, 781.94, 781.98, 2.37578],
[1481634480, 781.39, 781.94, 781.15, 781.94, 1.68882],
...
],
"180": [...],
"300": [...],
...
"604800": [...]
}
}
インタプリタ言語とかだと何も考えずにデシリアライズ可能なのですが、これを C# でデシリアライズしようとすると結構面倒です。
(もっと簡単な方法があれば教えていただきたいです)
まず、result
以下のキー(60
とか180
とか)がクエリに応じて動的に変わるので、Dictionary
を使用します。
また、値の部分が配列の入れ子になっているので、Dictionary
の Value
は List
の入れ子にします。
さらに最下層の Value が整数だったり浮動小数だったり、時刻は Unix タイムなので DateTime 型に変換したいとかあるので、List
の型は String
にしておき、後で個別にパースしていきます。
まずデシリアライズ用の型として以下のような構造体を定義します。
public struct CryptowatchResponse
{
public Dictionary<int, List<List<string>>> Result { get; set; }
}
以下のようにして Cryptowatch からデータを取得してデシリアライズします。
var before = DateTimeOffset.Now.ToUnixTimeSeconds();
CryptowatchResponse json;
using (var client = new HttpClient())
using (var response = client.GetAsync($"https://api.cryptowat.ch/markets/bitflyer/btcfxjpy/ohlc?before={before}").Result)
{
var contents = response.Content.ReadAsStringAsync().Result;
json = JsonConvert.DeserializeObject<CryptowatchResponse>(contents);
}
尚、ここでは HttpClient
を Using
していますが、普通はコンストラクタ等でインスタンス化して使いまわすようです。
(詳細はググってください)
さらに、ここから string
型で持っている値を適切な型に変換します。
(文字列のままで良ければこれは不要です)
まず各要素の値を入れるための構造体を以下のように定義します。
public struct CryptowatchData
{
public DateTime Time { get; set; }
public double Open { get; set; }
public double High { get; set; }
public double Low { get; set; }
public double Close { get; set; }
public double Volume { get; set; }
}
あとは愚直に変換してくだけです。
例えば、一分足のデータを変換したい場合は以下のような感じです。
var records = new List<CryptowatchData>();
foreach (var record in json.Result[60])
{
records.Add(new CryptowatchData()
{
// unixtime から JST に変換する
Time = DateTimeOffset.FromUnixTimeSeconds(long.Parse(record[0])).LocalDateTime,
Open = double.Parse(record[1]),
High = double.Parse(record[2]),
Low = double.Parse(record[3]),
Close = double.Parse(record[4]),
Volume = double.Parse(record[5]),
});
}
もっと簡単に書けないかなぁとは思いますけど、とりあえずこんな感じです。