├── FtxApi
├── Enums
│ ├── SideType.cs
│ ├── OrderType.cs
│ └── TriggerType.cs
├── Client.cs
├── Util
│ ├── Util.cs
│ └── FtxWebSockerRequestGenerator.cs
├── Properties
│ └── AssemblyInfo.cs
├── packages.config
├── FtxWebSocketApi.cs
├── FtxApi.csproj
└── FtxRestApi.cs
├── FtxApi_Test
├── App.config
├── Properties
│ └── AssemblyInfo.cs
├── FtxApi_Test.csproj
└── Program.cs
├── FtxApi.sln
└── .gitignore
/FtxApi/Enums/SideType.cs:
--------------------------------------------------------------------------------
1 | namespace FtxApi.Enums
2 | {
3 | public enum SideType : byte
4 | {
5 | buy,
6 | sell
7 | }
8 | }
--------------------------------------------------------------------------------
/FtxApi/Enums/OrderType.cs:
--------------------------------------------------------------------------------
1 | namespace FtxApi.Enums
2 | {
3 | public enum OrderType : byte
4 | {
5 | limit,
6 | market
7 | }
8 | }
--------------------------------------------------------------------------------
/FtxApi/Enums/TriggerType.cs:
--------------------------------------------------------------------------------
1 | namespace FtxApi.Enums
2 | {
3 | public enum TriggerType : byte
4 | {
5 | stop,
6 | trailingStop,
7 | takeProfit
8 | }
9 | }
--------------------------------------------------------------------------------
/FtxApi_Test/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/FtxApi/Client.cs:
--------------------------------------------------------------------------------
1 | namespace FtxApi
2 | {
3 | public class Client
4 | {
5 | public string ApiKey { get; }
6 |
7 | public string ApiSecret { get; }
8 |
9 | public Client()
10 | {
11 | ApiKey = "";
12 | ApiSecret = "";
13 | }
14 |
15 | public Client(string apiKey, string apiSecret)
16 | {
17 | ApiKey = apiKey;
18 | ApiSecret = apiSecret;
19 | }
20 |
21 | }
22 | }
--------------------------------------------------------------------------------
/FtxApi/Util/Util.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace FtxApi.Util
4 | {
5 | public static class Util
6 | {
7 | private static DateTime _epochTime = new DateTime(1970, 1, 1, 0, 0, 0);
8 |
9 | public static long GetMillisecondsFromEpochStart()
10 | {
11 | return GetMillisecondsFromEpochStart(DateTime.UtcNow);
12 | }
13 |
14 | public static long GetMillisecondsFromEpochStart(DateTime time)
15 | {
16 | return (long)(time - _epochTime).TotalMilliseconds;
17 | }
18 |
19 | public static long GetSecondsFromEpochStart(DateTime time)
20 | {
21 | return (long)(time - _epochTime).TotalSeconds;
22 | }
23 |
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/FtxApi/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("FtxApi")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("FtxApi")]
13 | [assembly: AssemblyCopyright("Copyright © 2019")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("0df6b050-bf66-4f89-af05-1376cd9c95b7")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/FtxApi_Test/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("FtxApi_Test")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("FtxApi_Test")]
13 | [assembly: AssemblyCopyright("Copyright © 2019")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("cac52920-ea13-47d8-98ac-c0776c6126fc")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/FtxApi/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/FtxApi.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.29318.209
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FtxApi", "FtxApi\FtxApi.csproj", "{0DF6B050-BF66-4F89-AF05-1376CD9C95B7}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FtxApi_Test", "FtxApi_Test\FtxApi_Test.csproj", "{CAC52920-EA13-47D8-98AC-C0776C6126FC}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|Any CPU = Debug|Any CPU
13 | Release|Any CPU = Release|Any CPU
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {0DF6B050-BF66-4F89-AF05-1376CD9C95B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17 | {0DF6B050-BF66-4F89-AF05-1376CD9C95B7}.Debug|Any CPU.Build.0 = Debug|Any CPU
18 | {0DF6B050-BF66-4F89-AF05-1376CD9C95B7}.Release|Any CPU.ActiveCfg = Release|Any CPU
19 | {0DF6B050-BF66-4F89-AF05-1376CD9C95B7}.Release|Any CPU.Build.0 = Release|Any CPU
20 | {CAC52920-EA13-47D8-98AC-C0776C6126FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {CAC52920-EA13-47D8-98AC-C0776C6126FC}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {CAC52920-EA13-47D8-98AC-C0776C6126FC}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {CAC52920-EA13-47D8-98AC-C0776C6126FC}.Release|Any CPU.Build.0 = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | GlobalSection(ExtensibilityGlobals) = postSolution
29 | SolutionGuid = {D979F2D7-80FA-4506-A097-7412C5C4D688}
30 | EndGlobalSection
31 | EndGlobal
32 |
--------------------------------------------------------------------------------
/FtxApi/Util/FtxWebSockerRequestGenerator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Security.Cryptography;
3 | using System.Text;
4 |
5 | namespace FtxApi
6 | {
7 | public static class FtxWebSockerRequestGenerator
8 | {
9 | public static string GetAuthRequest(Client client)
10 | {
11 | var time = Util.Util.GetMillisecondsFromEpochStart();
12 | var sig = GenerateSignature(client, time);
13 | var s = "{" +
14 | "\"args\": {" +
15 | $"\"key\": \"{client.ApiKey}\"," +
16 | $"\"sign\": \"{sig}\"," +
17 | $"\"time\": {time}" +
18 | "}," +
19 | "\"op\": \"login\"}";
20 | return s;
21 | }
22 |
23 | private static string GenerateSignature(Client client, long time)
24 | {
25 | var _hashMaker = new HMACSHA256(Encoding.UTF8.GetBytes(client.ApiSecret));
26 | var signature = $"{time}websocket_login";
27 | var hash = _hashMaker.ComputeHash(Encoding.UTF8.GetBytes(signature));
28 | var hashStringBase64 = BitConverter.ToString(hash).Replace("-", string.Empty);
29 | return hashStringBase64.ToLower();
30 | }
31 |
32 | public static string GetSubscribeRequest(string channel)
33 | {
34 | return $"{{\"op\": \"subscribe\", \"channel\": \"{channel}\"}}";
35 | }
36 |
37 | public static string GetUnsubscribeRequest(string channel)
38 | {
39 | return $"{{\"op\": \"unsubscribe\", \"channel\": \"{channel}\"}}";
40 | }
41 |
42 | public static string GetSubscribeRequest(string channel, string instrument)
43 | {
44 | return $"{{\"op\": \"subscribe\", \"channel\": \"{channel}\", \"market\": \"{instrument}\"}}";
45 | }
46 |
47 | public static string GetUnsubscribeRequest(string channel, string instrument)
48 | {
49 | return $"{{\"op\": \"unsubscribe\", \"channel\": \"{channel}\", \"market\": \"{instrument}\"}}";
50 | }
51 | }
52 | }
--------------------------------------------------------------------------------
/FtxApi_Test/FtxApi_Test.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {CAC52920-EA13-47D8-98AC-C0776C6126FC}
8 | Exe
9 | FtxApi_Test
10 | FtxApi_Test
11 | v4.7.2
12 | 512
13 | true
14 | true
15 |
16 |
17 | AnyCPU
18 | true
19 | full
20 | false
21 | bin\Debug\
22 | DEBUG;TRACE
23 | prompt
24 | 4
25 |
26 |
27 | AnyCPU
28 | pdbonly
29 | true
30 | bin\Release\
31 | TRACE
32 | prompt
33 | 4
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | {0df6b050-bf66-4f89-af05-1376cd9c95b7}
55 | FtxApi
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/FtxApi/FtxWebSocketApi.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Security.Authentication;
3 | using System.Threading.Tasks;
4 | using SuperSocket.ClientEngine;
5 | using WebSocket4Net;
6 |
7 | namespace FtxApi
8 | {
9 | public class FtxWebSocketApi
10 | {
11 | protected string _url;
12 |
13 | protected WebSocket _webSocketClient;
14 |
15 | public Action OnWebSocketConnect;
16 |
17 | public FtxWebSocketApi(string url)
18 | {
19 | _url = url;
20 | }
21 |
22 | public async Task Connect()
23 | {
24 | _webSocketClient = await CreateWebSocket(_url);
25 | OnWebSocketConnect?.Invoke();
26 | }
27 |
28 | protected async Task CreateWebSocket(string url)
29 | {
30 | var webSocket = new WebSocket(url);
31 |
32 | webSocket.Security.EnabledSslProtocols = SslProtocols.Tls12 | SslProtocols.Tls | SslProtocols.Tls12;
33 | webSocket.EnableAutoSendPing = false;
34 | webSocket.Opened += WebsocketOnOpen;
35 | webSocket.Error += WebSocketOnError;
36 | webSocket.Closed += WebsocketOnClosed;
37 | webSocket.MessageReceived += WebsocketOnMessageReceive;
38 | webSocket.DataReceived += OnDataRecieved;
39 | await OpenConnection(webSocket);
40 | return webSocket;
41 | }
42 |
43 | protected async Task OpenConnection(WebSocket webSocket)
44 | {
45 | webSocket.Open();
46 |
47 | while (webSocket.State != WebSocketState.Open)
48 | {
49 | await Task.Delay(25);
50 | }
51 | }
52 |
53 | public async Task Stop()
54 | {
55 | _webSocketClient.Opened -= WebsocketOnOpen;
56 | _webSocketClient.Error -= WebSocketOnError;
57 | _webSocketClient.Closed -= WebsocketOnClosed;
58 | _webSocketClient.MessageReceived -= WebsocketOnMessageReceive;
59 | _webSocketClient.DataReceived -= OnDataRecieved;
60 | _webSocketClient?.Close();
61 | _webSocketClient?.Dispose();
62 |
63 | while (_webSocketClient.State != WebSocketState.Closed) await Task.Delay(25);
64 | }
65 |
66 | protected void WebsocketOnOpen(object sender, EventArgs e)
67 | {
68 | Console.WriteLine($"OnOpen: {e}");
69 | }
70 |
71 | protected void WebSocketOnError(object sender, ErrorEventArgs e)
72 | {
73 | Console.WriteLine($"OnError: {e.Exception}");
74 | }
75 |
76 | protected void WebsocketOnClosed(object o, EventArgs e)
77 | {
78 | Console.WriteLine($"OnClosed: {e}");
79 | }
80 |
81 | protected void WebsocketOnMessageReceive(object o, MessageReceivedEventArgs messageReceivedEventArgs)
82 | {
83 | Console.WriteLine(messageReceivedEventArgs.Message);
84 | }
85 |
86 | private void OnDataRecieved(object sender, DataReceivedEventArgs e)
87 | {
88 | Console.WriteLine(e.Data.Length);
89 | }
90 |
91 | public void SendCommand(string command)
92 | {
93 | _webSocketClient?.Send(command);
94 | }
95 | }
96 | }
--------------------------------------------------------------------------------
/FtxApi_Test/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 | using FtxApi;
4 | using FtxApi.Enums;
5 |
6 | namespace FtxApi_Test
7 | {
8 | class Program
9 | {
10 | static void Main()
11 | {
12 | var client = new Client("_T68V7HmuHoiHlKmpUcOcbNOXkNWpzL-FvpO1VMa", "TsmQWQ4bXrOHzCVbD7vFzZtI-gs7j8tvh684hPY6");
13 | var api = new FtxRestApi(client);
14 | var wsApi = new FtxWebSocketApi("wss://ftx.com/ws/");
15 |
16 | RestTests(api).Wait();
17 | WebSocketTests(wsApi, client).Wait();
18 |
19 | Console.ReadLine();
20 | }
21 |
22 | private static async Task RestTests(FtxRestApi api)
23 | {
24 | var ins = "BTC-1227";
25 |
26 | var dateStart = DateTime.UtcNow.AddMinutes(-100);
27 | var dateEnd = DateTime.UtcNow.AddMinutes(-10);
28 |
29 | var r1 = api.GetCoinsAsync().Result;
30 | var r2 = api.GetAllFuturesAsync().Result;
31 | var r3 = api.GetFutureAsync(ins).Result;
32 | var r4 = api.GetFutureStatsAsync(ins).Result;
33 | var r5 = api.GetFundingRatesAsync(dateStart, dateEnd).Result;
34 | var r6 = api.GetHistoricalPricesAsync(ins, 300, 30, dateStart, dateEnd).Result;
35 | var r7 = api.GetMarketsAsync().Result;
36 | var r8 = api.GetSingleMarketsAsync(ins).Result;
37 | var r9 = api.GetMarketOrderBookAsync(ins, 20).Result;
38 | var r10 = api.GetMarketTradesAsync(ins, 20, dateStart, dateEnd).Result;
39 | var r11 = api.GetAccountInfoAsync().Result;
40 | var r12 = api.GetPositionsAsync().Result;
41 | var r13 = api.ChangeAccountLeverageAsync(20).Result;
42 | var r14 = api.GetCoinAsync().Result;
43 | var r15 = api.GetBalancesAsync().Result;
44 | var r16 = api.GetDepositAddressAsync("BTC").Result;
45 | var r17 = api.GetDepositHistoryAsync().Result;
46 | var r18 = api.GetWithdrawalHistoryAsync().Result;
47 | var r19 = api.RequestWithdrawalAsync("USDTBEAR", 20.2m, "0x83a127952d266A6eA306c40Ac62A4a70668FE3BE", "", "", "").Result;
48 | var r21 = api.GetOpenOrdersAsync(ins).Result;
49 | var r20 = api.PlaceOrderAsync(ins, SideType.buy, 1000, OrderType.limit, 0.001m, false).Result;
50 | var r20_1 = api.PlaceStopOrderAsync(ins, SideType.buy, 1000, 0.001m, false).Result;
51 | var r20_2 = api.PlaceTrailingStopOrderAsync(ins, SideType.buy, 0.05m, 0.001m, false).Result;
52 | var r20_3 = api.PlaceTakeProfitOrderAsync(ins, SideType.buy, 1000, 0.001m, false).Result;
53 | var r23 = api.GetOrderStatusAsync("12345").Result;
54 | var r24 = api.GetOrderStatusByClientIdAsync("12345").Result;
55 | var r25 = api.CancelOrderAsync("1234").Result;
56 | var r26 = api.CancelOrderByClientIdAsync("12345").Result;
57 | var r27 = api.CancelAllOrdersAsync(ins).Result;
58 | var r28 = api.GetFillsAsync(ins, 20, dateStart, dateEnd).Result;
59 | var r29 = api.GetFundingPaymentAsync(dateStart, dateEnd).Result;
60 | var r30 = api.GetLeveragedTokensListAsync().Result;
61 | var r31 = api.GetTokenInfoAsync("HEDGE").Result;
62 | var r32 = api.GetLeveragedTokenBalancesAsync().Result;
63 | var r33 = api.GetLeveragedTokenCreationListAsync().Result;
64 | var r34 = api.RequestLeveragedTokenCreationAsync("HEDGE", 100).Result;
65 | var r35 = api.GetLeveragedTokenRedemptionListAsync().Result;
66 | var r36 = api.RequestLeveragedTokenRedemptionAsync("HEDGE", 100).Result;
67 | }
68 |
69 | private static async Task WebSocketTests(FtxWebSocketApi wsApi, Client client)
70 | {
71 | var ins = "BTC-PERP";
72 |
73 | wsApi.OnWebSocketConnect += () =>
74 | {
75 | wsApi.SendCommand(FtxWebSockerRequestGenerator.GetAuthRequest(client));
76 | wsApi.SendCommand(FtxWebSockerRequestGenerator.GetSubscribeRequest("orderbook", ins));
77 | wsApi.SendCommand(FtxWebSockerRequestGenerator.GetSubscribeRequest("trades", ins));
78 | wsApi.SendCommand(FtxWebSockerRequestGenerator.GetSubscribeRequest("ticker", ins));
79 | wsApi.SendCommand(FtxWebSockerRequestGenerator.GetSubscribeRequest("fills"));
80 | wsApi.SendCommand(FtxWebSockerRequestGenerator.GetSubscribeRequest("orders"));
81 | };
82 |
83 | await wsApi.Connect();
84 | }
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Build results
17 | [Dd]ebug/
18 | [Dd]ebugPublic/
19 | [Rr]elease/
20 | [Rr]eleases/
21 | x64/
22 | x86/
23 | [Aa][Rr][Mm]/
24 | [Aa][Rr][Mm]64/
25 | bld/
26 | [Bb]in/
27 | [Oo]bj/
28 | [Ll]og/
29 |
30 | # Visual Studio 2015/2017 cache/options directory
31 | .vs/
32 | # Uncomment if you have tasks that create the project's static files in wwwroot
33 | #wwwroot/
34 |
35 | # Visual Studio 2017 auto generated files
36 | Generated\ Files/
37 |
38 | # MSTest test Results
39 | [Tt]est[Rr]esult*/
40 | [Bb]uild[Ll]og.*
41 |
42 | # NUNIT
43 | *.VisualState.xml
44 | TestResult.xml
45 |
46 | # Build Results of an ATL Project
47 | [Dd]ebugPS/
48 | [Rr]eleasePS/
49 | dlldata.c
50 |
51 | # Benchmark Results
52 | BenchmarkDotNet.Artifacts/
53 |
54 | # .NET Core
55 | project.lock.json
56 | project.fragment.lock.json
57 | artifacts/
58 |
59 | # StyleCop
60 | StyleCopReport.xml
61 |
62 | # Files built by Visual Studio
63 | *_i.c
64 | *_p.c
65 | *_h.h
66 | *.ilk
67 | *.meta
68 | *.obj
69 | *.iobj
70 | *.pch
71 | *.pdb
72 | *.ipdb
73 | *.pgc
74 | *.pgd
75 | *.rsp
76 | *.sbr
77 | *.tlb
78 | *.tli
79 | *.tlh
80 | *.tmp
81 | *.tmp_proj
82 | *_wpftmp.csproj
83 | *.log
84 | *.vspscc
85 | *.vssscc
86 | .builds
87 | *.pidb
88 | *.svclog
89 | *.scc
90 |
91 | # Chutzpah Test files
92 | _Chutzpah*
93 |
94 | # Visual C++ cache files
95 | ipch/
96 | *.aps
97 | *.ncb
98 | *.opendb
99 | *.opensdf
100 | *.sdf
101 | *.cachefile
102 | *.VC.db
103 | *.VC.VC.opendb
104 |
105 | # Visual Studio profiler
106 | *.psess
107 | *.vsp
108 | *.vspx
109 | *.sap
110 |
111 | # Visual Studio Trace Files
112 | *.e2e
113 |
114 | # TFS 2012 Local Workspace
115 | $tf/
116 |
117 | # Guidance Automation Toolkit
118 | *.gpState
119 |
120 | # ReSharper is a .NET coding add-in
121 | _ReSharper*/
122 | *.[Rr]e[Ss]harper
123 | *.DotSettings.user
124 |
125 | # JustCode is a .NET coding add-in
126 | .JustCode
127 |
128 | # TeamCity is a build add-in
129 | _TeamCity*
130 |
131 | # DotCover is a Code Coverage Tool
132 | *.dotCover
133 |
134 | # AxoCover is a Code Coverage Tool
135 | .axoCover/*
136 | !.axoCover/settings.json
137 |
138 | # Visual Studio code coverage results
139 | *.coverage
140 | *.coveragexml
141 |
142 | # NCrunch
143 | _NCrunch_*
144 | .*crunch*.local.xml
145 | nCrunchTemp_*
146 |
147 | # MightyMoose
148 | *.mm.*
149 | AutoTest.Net/
150 |
151 | # Web workbench (sass)
152 | .sass-cache/
153 |
154 | # Installshield output folder
155 | [Ee]xpress/
156 |
157 | # DocProject is a documentation generator add-in
158 | DocProject/buildhelp/
159 | DocProject/Help/*.HxT
160 | DocProject/Help/*.HxC
161 | DocProject/Help/*.hhc
162 | DocProject/Help/*.hhk
163 | DocProject/Help/*.hhp
164 | DocProject/Help/Html2
165 | DocProject/Help/html
166 |
167 | # Click-Once directory
168 | publish/
169 |
170 | # Publish Web Output
171 | *.[Pp]ublish.xml
172 | *.azurePubxml
173 | # Note: Comment the next line if you want to checkin your web deploy settings,
174 | # but database connection strings (with potential passwords) will be unencrypted
175 | *.pubxml
176 | *.publishproj
177 |
178 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
179 | # checkin your Azure Web App publish settings, but sensitive information contained
180 | # in these scripts will be unencrypted
181 | PublishScripts/
182 |
183 | # NuGet Packages
184 | *.nupkg
185 | # The packages folder can be ignored because of Package Restore
186 | **/[Pp]ackages/*
187 | # except build/, which is used as an MSBuild target.
188 | !**/[Pp]ackages/build/
189 | # Uncomment if necessary however generally it will be regenerated when needed
190 | #!**/[Pp]ackages/repositories.config
191 | # NuGet v3's project.json files produces more ignorable files
192 | *.nuget.props
193 | *.nuget.targets
194 |
195 | # Microsoft Azure Build Output
196 | csx/
197 | *.build.csdef
198 |
199 | # Microsoft Azure Emulator
200 | ecf/
201 | rcf/
202 |
203 | # Windows Store app package directories and files
204 | AppPackages/
205 | BundleArtifacts/
206 | Package.StoreAssociation.xml
207 | _pkginfo.txt
208 | *.appx
209 |
210 | # Visual Studio cache files
211 | # files ending in .cache can be ignored
212 | *.[Cc]ache
213 | # but keep track of directories ending in .cache
214 | !?*.[Cc]ache/
215 |
216 | # Others
217 | ClientBin/
218 | ~$*
219 | *~
220 | *.dbmdl
221 | *.dbproj.schemaview
222 | *.jfm
223 | *.pfx
224 | *.publishsettings
225 | orleans.codegen.cs
226 |
227 | # Including strong name files can present a security risk
228 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
229 | #*.snk
230 |
231 | # Since there are multiple workflows, uncomment next line to ignore bower_components
232 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
233 | #bower_components/
234 |
235 | # RIA/Silverlight projects
236 | Generated_Code/
237 |
238 | # Backup & report files from converting an old project file
239 | # to a newer Visual Studio version. Backup files are not needed,
240 | # because we have git ;-)
241 | _UpgradeReport_Files/
242 | Backup*/
243 | UpgradeLog*.XML
244 | UpgradeLog*.htm
245 | ServiceFabricBackup/
246 | *.rptproj.bak
247 |
248 | # SQL Server files
249 | *.mdf
250 | *.ldf
251 | *.ndf
252 |
253 | # Business Intelligence projects
254 | *.rdl.data
255 | *.bim.layout
256 | *.bim_*.settings
257 | *.rptproj.rsuser
258 | *- Backup*.rdl
259 |
260 | # Microsoft Fakes
261 | FakesAssemblies/
262 |
263 | # GhostDoc plugin setting file
264 | *.GhostDoc.xml
265 |
266 | # Node.js Tools for Visual Studio
267 | .ntvs_analysis.dat
268 | node_modules/
269 |
270 | # Visual Studio 6 build log
271 | *.plg
272 |
273 | # Visual Studio 6 workspace options file
274 | *.opt
275 |
276 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
277 | *.vbw
278 |
279 | # Visual Studio LightSwitch build output
280 | **/*.HTMLClient/GeneratedArtifacts
281 | **/*.DesktopClient/GeneratedArtifacts
282 | **/*.DesktopClient/ModelManifest.xml
283 | **/*.Server/GeneratedArtifacts
284 | **/*.Server/ModelManifest.xml
285 | _Pvt_Extensions
286 |
287 | # Paket dependency manager
288 | .paket/paket.exe
289 | paket-files/
290 |
291 | # FAKE - F# Make
292 | .fake/
293 |
294 | # JetBrains Rider
295 | .idea/
296 | *.sln.iml
297 |
298 | # CodeRush personal settings
299 | .cr/personal
300 |
301 | # Python Tools for Visual Studio (PTVS)
302 | __pycache__/
303 | *.pyc
304 |
305 | # Cake - Uncomment if you are using it
306 | # tools/**
307 | # !tools/packages.config
308 |
309 | # Tabs Studio
310 | *.tss
311 |
312 | # Telerik's JustMock configuration file
313 | *.jmconfig
314 |
315 | # BizTalk build output
316 | *.btp.cs
317 | *.btm.cs
318 | *.odx.cs
319 | *.xsd.cs
320 |
321 | # OpenCover UI analysis results
322 | OpenCover/
323 |
324 | # Azure Stream Analytics local run output
325 | ASALocalRun/
326 |
327 | # MSBuild Binary and Structured Log
328 | *.binlog
329 |
330 | # NVidia Nsight GPU debugger configuration file
331 | *.nvuser
332 |
333 | # MFractors (Xamarin productivity tool) working folder
334 | .mfractor/
335 |
336 | # Local History for Visual Studio
337 | .localhistory/
338 |
339 | # BeatPulse healthcheck temp database
340 | healthchecksdb
341 |
--------------------------------------------------------------------------------
/FtxApi/FtxApi.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {0DF6B050-BF66-4F89-AF05-1376CD9C95B7}
8 | Library
9 | Properties
10 | FtxApi
11 | FtxApi
12 | v4.7.2
13 | 512
14 | true
15 |
16 |
17 | true
18 | full
19 | false
20 | bin\Debug\
21 | DEBUG;TRACE
22 | prompt
23 | 4
24 |
25 |
26 | pdbonly
27 | true
28 | bin\Release\
29 | TRACE
30 | prompt
31 | 4
32 |
33 |
34 |
35 | ..\packages\SuperSocket.ClientEngine.Core.0.10.0\lib\net45\SuperSocket.ClientEngine.dll
36 |
37 |
38 |
39 | ..\packages\System.Collections.Specialized.4.3.0\lib\net46\System.Collections.Specialized.dll
40 | True
41 | True
42 |
43 |
44 |
45 |
46 | ..\packages\System.IO.4.3.0\lib\net462\System.IO.dll
47 | True
48 | True
49 |
50 |
51 | ..\packages\System.Linq.4.3.0\lib\net463\System.Linq.dll
52 | True
53 | True
54 |
55 |
56 | ..\packages\System.Net.NameResolution.4.3.0\lib\net46\System.Net.NameResolution.dll
57 | True
58 | True
59 |
60 |
61 | ..\packages\System.Net.Security.4.3.0\lib\net46\System.Net.Security.dll
62 | True
63 | True
64 |
65 |
66 | ..\packages\System.Net.Sockets.4.3.0\lib\net46\System.Net.Sockets.dll
67 | True
68 | True
69 |
70 |
71 | ..\packages\System.Runtime.4.3.0\lib\net462\System.Runtime.dll
72 | True
73 | True
74 |
75 |
76 | ..\packages\System.Runtime.Extensions.4.3.0\lib\net462\System.Runtime.Extensions.dll
77 | True
78 | True
79 |
80 |
81 | ..\packages\System.Runtime.InteropServices.4.3.0\lib\net463\System.Runtime.InteropServices.dll
82 | True
83 | True
84 |
85 |
86 | ..\packages\System.Security.Cryptography.Algorithms.4.3.0\lib\net463\System.Security.Cryptography.Algorithms.dll
87 | True
88 | True
89 |
90 |
91 | ..\packages\System.Security.Cryptography.Encoding.4.3.0\lib\net46\System.Security.Cryptography.Encoding.dll
92 | True
93 | True
94 |
95 |
96 | ..\packages\System.Security.Cryptography.Primitives.4.3.0\lib\net46\System.Security.Cryptography.Primitives.dll
97 | True
98 | True
99 |
100 |
101 | ..\packages\System.Security.Cryptography.X509Certificates.4.3.0\lib\net461\System.Security.Cryptography.X509Certificates.dll
102 | True
103 | True
104 |
105 |
106 | ..\packages\System.Text.RegularExpressions.4.3.0\lib\net463\System.Text.RegularExpressions.dll
107 | True
108 | True
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 | ..\packages\WebSocket4Net.0.15.2\lib\net45\WebSocket4Net.dll
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
--------------------------------------------------------------------------------
/FtxApi/FtxRestApi.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net.Http;
3 | using System.Security.Cryptography;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using FtxApi.Enums;
7 |
8 | namespace FtxApi
9 | {
10 | public class FtxRestApi
11 | {
12 | private const string Url = "https://ftx.com/";
13 |
14 | private readonly Client _client;
15 |
16 | private readonly HttpClient _httpClient;
17 |
18 | private readonly HMACSHA256 _hashMaker;
19 |
20 | private long _nonce;
21 |
22 | public FtxRestApi(Client client)
23 | {
24 | _client = client;
25 | _httpClient = new HttpClient
26 | {
27 | BaseAddress = new Uri(Url),
28 | Timeout = TimeSpan.FromSeconds(30)
29 | };
30 |
31 | _hashMaker = new HMACSHA256(Encoding.UTF8.GetBytes(_client.ApiSecret));
32 | }
33 |
34 | #region Coins
35 |
36 | public async Task GetCoinsAsync()
37 | {
38 | var resultString = $"api/coins";
39 |
40 | var result = await CallAsync(HttpMethod.Get, resultString);
41 |
42 | return ParseResponce(result);
43 | }
44 |
45 | #endregion
46 |
47 | #region Futures
48 |
49 | public async Task GetAllFuturesAsync()
50 | {
51 | var resultString = $"api/futures";
52 |
53 | var result = await CallAsync(HttpMethod.Get, resultString);
54 |
55 | return ParseResponce(result);
56 | }
57 |
58 | public async Task GetFutureAsync(string future)
59 | {
60 | var resultString = $"api/futures/{future}";
61 |
62 | var result = await CallAsync(HttpMethod.Get, resultString);
63 |
64 | return ParseResponce(result);
65 | }
66 |
67 | public async Task GetFutureStatsAsync(string future)
68 | {
69 | var resultString = $"api/futures/{future}/stats";
70 |
71 | var result = await CallAsync(HttpMethod.Get, resultString);
72 |
73 | return ParseResponce(result);
74 | }
75 |
76 | public async Task GetFundingRatesAsync(DateTime start, DateTime end)
77 | {
78 | var resultString = $"api/funding_rates?start_time={Util.Util.GetSecondsFromEpochStart(start)}&end_time={Util.Util.GetSecondsFromEpochStart(end)}";
79 |
80 | var result = await CallAsync(HttpMethod.Get, resultString);
81 |
82 | return ParseResponce(result);
83 | }
84 |
85 | public async Task GetHistoricalPricesAsync(string futureName, int resolution, int limit, DateTime start, DateTime end)
86 | {
87 | var resultString = $"api/futures/{futureName}/mark_candles?resolution={resolution}&limit={limit}&start_time={Util.Util.GetSecondsFromEpochStart(start)}&end_time={Util.Util.GetSecondsFromEpochStart(end)}";
88 |
89 | var result = await CallAsync(HttpMethod.Get, resultString);
90 |
91 | return ParseResponce(result);
92 | }
93 |
94 | #endregion
95 |
96 | #region Markets
97 |
98 | public async Task GetMarketsAsync()
99 | {
100 | var resultString = $"api/markets";
101 |
102 | var result = await CallAsync(HttpMethod.Get, resultString);
103 |
104 | return ParseResponce(result);
105 | }
106 |
107 | public async Task GetSingleMarketsAsync(string marketName)
108 | {
109 | var resultString = $"api/markets/{marketName}";
110 |
111 | var result = await CallAsync(HttpMethod.Get, resultString);
112 |
113 | return ParseResponce(result);
114 | }
115 |
116 | public async Task GetMarketOrderBookAsync(string marketName, int depth = 20)
117 | {
118 | var resultString = $"api/markets/{marketName}/orderbook?depth={depth}";
119 |
120 | var result = await CallAsync(HttpMethod.Get, resultString);
121 |
122 | return ParseResponce(result);
123 | }
124 |
125 | public async Task GetMarketTradesAsync(string marketName, int limit, DateTime start, DateTime end)
126 | {
127 | var resultString = $"api/markets/{marketName}/trades?limit={limit}&start_time={Util.Util.GetSecondsFromEpochStart(start)}&end_time={Util.Util.GetSecondsFromEpochStart(end)}";
128 |
129 | var result = await CallAsync(HttpMethod.Get, resultString);
130 |
131 | return ParseResponce(result);
132 | }
133 |
134 | #endregion
135 |
136 | #region Account
137 |
138 | public async Task GetAccountInfoAsync()
139 | {
140 | var resultString = $"api/account";
141 | var sign = GenerateSignature(HttpMethod.Get, "/api/account", "");
142 |
143 | var result = await CallAsyncSign(HttpMethod.Get, resultString, sign);
144 |
145 | return ParseResponce(result);
146 | }
147 |
148 |
149 | public async Task GetPositionsAsync()
150 | {
151 | var resultString = $"api/positions";
152 | var sign = GenerateSignature(HttpMethod.Get, "/api/positions", "");
153 |
154 | var result = await CallAsyncSign(HttpMethod.Get, resultString, sign);
155 |
156 | return ParseResponce(result);
157 | }
158 |
159 | public async Task ChangeAccountLeverageAsync(int leverage)
160 | {
161 | var resultString = $"api/account/leverage";
162 |
163 | var body = $"{{\"leverage\": {leverage}}}";
164 |
165 | var sign = GenerateSignature(HttpMethod.Post, "/api/account/leverage", body);
166 |
167 | var result = await CallAsyncSign(HttpMethod.Post, resultString, sign, body);
168 |
169 | return ParseResponce(result);
170 | }
171 |
172 | #endregion
173 |
174 | #region Wallet
175 |
176 | public async Task GetCoinAsync()
177 | {
178 | var resultString = $"api/wallet/coins";
179 |
180 | var sign = GenerateSignature(HttpMethod.Get, "/api/wallet/coins", "");
181 |
182 | var result = await CallAsyncSign(HttpMethod.Get, resultString, sign);
183 |
184 | return ParseResponce(result);
185 | }
186 |
187 | public async Task GetBalancesAsync()
188 | {
189 | var resultString = $"api/wallet/balances";
190 |
191 | var sign = GenerateSignature(HttpMethod.Get, "/api/wallet/balances", "");
192 |
193 | var result = await CallAsyncSign(HttpMethod.Get, resultString, sign);
194 |
195 | return ParseResponce(result);
196 | }
197 |
198 | public async Task GetDepositAddressAsync(string coin)
199 | {
200 | var resultString = $"api/wallet/deposit_address/{coin}";
201 |
202 | var sign = GenerateSignature(HttpMethod.Get, $"/api/wallet/deposit_address/{coin}", "");
203 |
204 | var result = await CallAsyncSign(HttpMethod.Get, resultString, sign);
205 |
206 | return ParseResponce(result);
207 | }
208 |
209 | public async Task GetDepositHistoryAsync()
210 | {
211 | var resultString = $"api/wallet/deposits";
212 |
213 | var sign = GenerateSignature(HttpMethod.Get, "/api/wallet/deposits", "");
214 |
215 | var result = await CallAsyncSign(HttpMethod.Get, resultString, sign);
216 |
217 | return ParseResponce(result);
218 | }
219 |
220 | public async Task GetWithdrawalHistoryAsync()
221 | {
222 | var resultString = $"api/wallet/withdrawals";
223 |
224 | var sign = GenerateSignature(HttpMethod.Get, "/api/wallet/withdrawals", "");
225 |
226 | var result = await CallAsyncSign(HttpMethod.Get, resultString, sign);
227 |
228 | return ParseResponce(result);
229 | }
230 |
231 | public async Task RequestWithdrawalAsync(string coin, decimal size, string addr, string tag, string pass, string code)
232 | {
233 | var resultString = $"api/wallet/withdrawals";
234 |
235 | var body = $"{{"+
236 | $"\"coin\": \"{coin}\"," +
237 | $"\"size\": {size},"+
238 | $"\"address\": \"{addr}\","+
239 | $"\"tag\": {tag},"+
240 | $"\"password\": \"{pass}\","+
241 | $"\"code\": {code}" +
242 | "}";
243 |
244 | var sign = GenerateSignature(HttpMethod.Post, "/api/wallet/withdrawals", body);
245 |
246 | var result = await CallAsyncSign(HttpMethod.Post, resultString, sign, body);
247 |
248 | return ParseResponce(result);
249 | }
250 |
251 | #endregion
252 |
253 | #region Orders
254 |
255 | public async Task PlaceOrderAsync(string instrument, SideType side, decimal price, OrderType orderType, decimal amount, bool reduceOnly = false)
256 | {
257 | var path = $"api/orders";
258 |
259 | var body =
260 | $"{{\"market\": \"{instrument}\"," +
261 | $"\"side\": \"{side.ToString()}\"," +
262 | $"\"price\": {price}," +
263 | $"\"type\": \"{orderType.ToString()}\"," +
264 | $"\"size\": {amount}," +
265 | $"\"reduceOnly\": {reduceOnly.ToString().ToLower()}}}";
266 |
267 | var sign = GenerateSignature(HttpMethod.Post, "/api/orders", body);
268 | var result = await CallAsyncSign(HttpMethod.Post, path, sign, body);
269 |
270 | return ParseResponce(result);
271 | }
272 |
273 | public async Task PlaceStopOrderAsync(string instrument, SideType side, decimal triggerPrice, decimal amount, bool reduceOnly = false)
274 | {
275 | var path = $"api/conditional_orders";
276 |
277 | var body =
278 | $"{{\"market\": \"{instrument}\"," +
279 | $"\"side\": \"{side.ToString()}\"," +
280 | $"\"triggerPrice\": {triggerPrice}," +
281 | $"\"type\": \"stop\"," +
282 | $"\"size\": {amount}," +
283 | $"\"reduceOnly\": {reduceOnly.ToString().ToLower()}}}";
284 |
285 | var sign = GenerateSignature(HttpMethod.Post, "/api/conditional_orders", body);
286 | var result = await CallAsyncSign(HttpMethod.Post, path, sign, body);
287 |
288 | return ParseResponce(result);
289 | }
290 |
291 | public async Task PlaceTrailingStopOrderAsync(string instrument, SideType side, decimal trailValue, decimal amount, bool reduceOnly = false)
292 | {
293 | var path = $"api/conditional_orders";
294 |
295 | var body =
296 | $"{{\"market\": \"{instrument}\"," +
297 | $"\"side\": \"{side.ToString()}\"," +
298 | $"\"trailValue\": {trailValue}," +
299 | $"\"type\": \"trailingStop\"," +
300 | $"\"size\": {amount}," +
301 | $"\"reduceOnly\": {reduceOnly.ToString().ToLower()}}}";
302 |
303 | var sign = GenerateSignature(HttpMethod.Post, "/api/conditional_orders", body);
304 | var result = await CallAsyncSign(HttpMethod.Post, path, sign, body);
305 |
306 | return ParseResponce(result);
307 | }
308 |
309 | public async Task PlaceTakeProfitOrderAsync(string instrument, SideType side, decimal triggerPrice, decimal amount, bool reduceOnly = false)
310 | {
311 | var path = $"api/conditional_orders";
312 |
313 | var body =
314 | $"{{\"market\": \"{instrument}\"," +
315 | $"\"side\": \"{side.ToString()}\"," +
316 | $"\"triggerPrice\": {triggerPrice}," +
317 | $"\"type\": \"takeProfit\"," +
318 | $"\"size\": {amount}," +
319 | $"\"reduceOnly\": {reduceOnly.ToString().ToLower()}}}";
320 |
321 | var sign = GenerateSignature(HttpMethod.Post, "/api/conditional_orders", body);
322 | var result = await CallAsyncSign(HttpMethod.Post, path, sign, body);
323 |
324 | return ParseResponce(result);
325 | }
326 |
327 | public async Task GetOpenOrdersAsync(string instrument)
328 | {
329 | var path = $"api/orders?market={instrument}";
330 |
331 | var sign = GenerateSignature(HttpMethod.Get, $"/api/orders?market={instrument}", "");
332 |
333 | var result = await CallAsyncSign(HttpMethod.Get, path, sign);
334 |
335 | return ParseResponce(result);
336 | }
337 |
338 | public async Task GetOrderStatusAsync(string id)
339 | {
340 | var resultString = $"api/orders/{id}";
341 |
342 | var sign = GenerateSignature(HttpMethod.Get, $"/api/orders/{id}", "");
343 |
344 | var result = await CallAsyncSign(HttpMethod.Get, resultString, sign);
345 |
346 | return ParseResponce(result);
347 | }
348 |
349 | public async Task GetOrderStatusByClientIdAsync(string clientOrderId)
350 | {
351 | var resultString = $"api/orders/by_client_id/{clientOrderId}";
352 |
353 | var sign = GenerateSignature(HttpMethod.Get, $"/api/orders/by_client_id/{clientOrderId}", "");
354 |
355 | var result = await CallAsyncSign(HttpMethod.Get, resultString, sign);
356 |
357 | return ParseResponce(result);
358 | }
359 |
360 | public async Task CancelOrderAsync(string id)
361 | {
362 | var resultString = $"api/orders/{id}";
363 |
364 | var sign = GenerateSignature(HttpMethod.Delete, $"/api/orders/{id}", "");
365 |
366 | var result = await CallAsyncSign(HttpMethod.Delete, resultString, sign);
367 |
368 | return ParseResponce(result);
369 | }
370 |
371 | public async Task CancelOrderByClientIdAsync(string clientOrderId)
372 | {
373 | var resultString = $"api/orders/by_client_id/{clientOrderId}";
374 |
375 | var sign = GenerateSignature(HttpMethod.Delete, $"/api/orders/by_client_id/{clientOrderId}", "");
376 |
377 | var result = await CallAsyncSign(HttpMethod.Delete, resultString, sign);
378 |
379 | return ParseResponce(result);
380 | }
381 |
382 | public async Task CancelAllOrdersAsync(string instrument)
383 | {
384 | var resultString = $"api/orders";
385 |
386 | var body =
387 | $"{{\"market\": \"{instrument}\"}}";
388 |
389 | var sign = GenerateSignature(HttpMethod.Delete, $"/api/orders", body);
390 |
391 | var result = await CallAsyncSign(HttpMethod.Delete, resultString, sign, body);
392 |
393 | return ParseResponce(result);
394 | }
395 |
396 | #endregion
397 |
398 | #region Fills
399 |
400 | public async Task GetFillsAsync(string market, int limit, DateTime start, DateTime end)
401 | {
402 | var resultString = $"api/fills?market={market}&limit={limit}&start_time={Util.Util.GetSecondsFromEpochStart(start)}&end_time={Util.Util.GetSecondsFromEpochStart(end)}";
403 |
404 | var sign = GenerateSignature(HttpMethod.Get, $"/{resultString}", "");
405 |
406 | var result = await CallAsyncSign(HttpMethod.Get, resultString, sign);
407 |
408 | return ParseResponce(result);
409 | }
410 |
411 | #endregion
412 |
413 | #region Funding
414 |
415 | public async Task GetFundingPaymentAsync(DateTime start, DateTime end)
416 | {
417 | var resultString = $"api/funding_payments?start_time={Util.Util.GetSecondsFromEpochStart(start)}&end_time={Util.Util.GetSecondsFromEpochStart(end)}";
418 |
419 | var sign = GenerateSignature(HttpMethod.Get, $"/{resultString}", "");
420 |
421 | var result = await CallAsyncSign(HttpMethod.Get, resultString, sign);
422 |
423 | return ParseResponce(result);
424 | }
425 |
426 | #endregion
427 |
428 | #region Leveraged Tokens
429 |
430 | public async Task GetLeveragedTokensListAsync()
431 | {
432 | var resultString = $"api/lt/tokens";
433 |
434 | var result = await CallAsync(HttpMethod.Get, resultString);
435 |
436 | return ParseResponce(result);
437 | }
438 |
439 | public async Task GetTokenInfoAsync(string tokenName)
440 | {
441 | var resultString = $"api/lt/{tokenName}";
442 |
443 | var result = await CallAsync(HttpMethod.Get, resultString);
444 |
445 | return ParseResponce(result);
446 | }
447 |
448 | public async Task GetLeveragedTokenBalancesAsync()
449 | {
450 | var resultString = $"api/lt/balances";
451 |
452 | var sign = GenerateSignature(HttpMethod.Get, $"/api/lt/balances", "");
453 |
454 | var result = await CallAsyncSign(HttpMethod.Get, resultString, sign);
455 |
456 | return ParseResponce(result);
457 | }
458 |
459 | public async Task GetLeveragedTokenCreationListAsync()
460 | {
461 | var resultString = $"api/lt/creations";
462 |
463 | var sign = GenerateSignature(HttpMethod.Get, $"/api/lt/creations", "");
464 |
465 | var result = await CallAsyncSign(HttpMethod.Get, resultString, sign);
466 |
467 | return ParseResponce(result);
468 | }
469 |
470 | public async Task RequestLeveragedTokenCreationAsync(string tokenName, decimal size)
471 | {
472 | var resultString = $"api/lt/{tokenName}/create";
473 |
474 | var body = $"{{\"size\": {size}}}";
475 |
476 | var sign = GenerateSignature(HttpMethod.Post, $"/api/lt/{tokenName}/create", body);
477 |
478 | var result = await CallAsyncSign(HttpMethod.Post, resultString, sign, body);
479 |
480 | return ParseResponce(result);
481 | }
482 |
483 | public async Task GetLeveragedTokenRedemptionListAsync()
484 | {
485 | var resultString = $"api/lt/redemptions";
486 |
487 | var sign = GenerateSignature(HttpMethod.Get, $"/api/lt/redemptions", "");
488 |
489 | var result = await CallAsyncSign(HttpMethod.Get, resultString, sign);
490 |
491 | return ParseResponce(result);
492 | }
493 |
494 | public async Task RequestLeveragedTokenRedemptionAsync(string tokenName, decimal size)
495 | {
496 | var resultString = $"api/lt/{tokenName}/redeem";
497 |
498 | var body = $"{{\"size\": {size}}}";
499 |
500 | var sign = GenerateSignature(HttpMethod.Post, $"/api/lt/{tokenName}/redeem", body);
501 |
502 | var result = await CallAsyncSign(HttpMethod.Post, resultString, sign, body);
503 |
504 | return ParseResponce(result);
505 | }
506 |
507 | #endregion
508 |
509 | #region Util
510 |
511 | private async Task CallAsync(HttpMethod method, string endpoint, string body = null)
512 | {
513 | var request = new HttpRequestMessage(method, endpoint);
514 |
515 | if (body != null)
516 | {
517 | request.Content = new StringContent(body, Encoding.UTF8, "application/json");
518 | }
519 |
520 | var response = await _httpClient.SendAsync(request).ConfigureAwait(false);
521 |
522 | var result = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
523 |
524 | return result;
525 | }
526 |
527 | private async Task CallAsyncSign(HttpMethod method, string endpoint, string sign, string body = null)
528 | {
529 | var request = new HttpRequestMessage(method, endpoint);
530 |
531 | if (body != null)
532 | {
533 | request.Content = new StringContent(body, Encoding.UTF8, "application/json");
534 | }
535 |
536 | request.Headers.Add("FTX-KEY", _client.ApiKey);
537 | request.Headers.Add("FTX-SIGN", sign);
538 | request.Headers.Add("FTX-TS", _nonce.ToString());
539 |
540 | var response = await _httpClient.SendAsync(request).ConfigureAwait(false);
541 |
542 | var result = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
543 |
544 | return result;
545 | }
546 |
547 | private string GenerateSignature(HttpMethod method, string url, string requestBody)
548 | {
549 | _nonce = GetNonce();
550 | var signature = $"{_nonce}{method.ToString().ToUpper()}{url}{requestBody}";
551 | var hash = _hashMaker.ComputeHash(Encoding.UTF8.GetBytes(signature));
552 | var hashStringBase64 = BitConverter.ToString(hash).Replace("-", string.Empty);
553 | return hashStringBase64.ToLower();
554 | }
555 |
556 | private long GetNonce()
557 | {
558 | return Util.Util.GetMillisecondsFromEpochStart();
559 | }
560 |
561 | private dynamic ParseResponce(string responce)
562 | {
563 | return (dynamic)responce;
564 | }
565 |
566 |
567 | #endregion
568 | }
569 | }
--------------------------------------------------------------------------------