├── .gitattributes
├── .gitignore
├── CCXT
├── CCXT.pyproj
├── CCXTException.py
├── NamedPipes.py
├── __init__.py
├── ccxtAPI.py
├── ccxtAPI.spec
└── test.py
├── CCXTSharp.sln
├── CCXTSharp
├── Balances.cs
├── CCXTException.cs
├── CCXTSharp.csproj
├── CCXTSharp.nuspec
├── CcxtAPI.Message.cs
├── CcxtAPI.cs
├── Market.cs
├── NamedPipe.cs
├── Properties
│ └── AssemblyInfo.cs
├── nuget.exe
├── nugetPushCommand.txt
└── packages.config
├── CCXTSharpTest
├── App.config
├── CCXTSharpTest.csproj
├── Program.cs
└── Properties
│ └── AssemblyInfo.cs
├── Example
├── App.config
├── Example.csproj
├── Program.cs
├── Properties
│ └── AssemblyInfo.cs
├── ccxt
│ ├── ccxtAPI.exe
│ └── scripts
│ │ ├── CCXTException.py
│ │ ├── NamedPipes.py
│ │ └── ccxtAPI.py
└── packages.config
├── LICENSE
└── README.md
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/.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 | #project specific
7 | /CCXT/build
8 | /CCXT/dist
9 |
10 | # User-specific files
11 | *.suo
12 | *.user
13 | *.userosscache
14 | *.sln.docstates
15 |
16 | # User-specific files (MonoDevelop/Xamarin Studio)
17 | *.userprefs
18 |
19 | # Build results
20 | [Dd]ebug/
21 | [Dd]ebugPublic/
22 | [Rr]elease/
23 | [Rr]eleases/
24 | x64/
25 | x86/
26 | bld/
27 | [Bb]in/
28 | [Oo]bj/
29 | [Ll]og/
30 |
31 | # Visual Studio 2015/2017 cache/options directory
32 | .vs/
33 | # Uncomment if you have tasks that create the project's static files in wwwroot
34 | #wwwroot/
35 |
36 | # Visual Studio 2017 auto generated files
37 | Generated\ Files/
38 |
39 | # MSTest test Results
40 | [Tt]est[Rr]esult*/
41 | [Bb]uild[Ll]og.*
42 |
43 | # NUNIT
44 | *.VisualState.xml
45 | TestResult.xml
46 |
47 | # Build Results of an ATL Project
48 | [Dd]ebugPS/
49 | [Rr]eleasePS/
50 | dlldata.c
51 |
52 | # Benchmark Results
53 | BenchmarkDotNet.Artifacts/
54 |
55 | # .NET Core
56 | project.lock.json
57 | project.fragment.lock.json
58 | artifacts/
59 |
60 | # StyleCop
61 | StyleCopReport.xml
62 |
63 | # Files built by Visual Studio
64 | *_i.c
65 | *_p.c
66 | *_h.h
67 | *.ilk
68 | *.meta
69 | *.obj
70 | *.iobj
71 | *.pch
72 | *.pdb
73 | *.ipdb
74 | *.pgc
75 | *.pgd
76 | *.rsp
77 | *.sbr
78 | *.tlb
79 | *.tli
80 | *.tlh
81 | *.tmp
82 | *.tmp_proj
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 |
259 | # Microsoft Fakes
260 | FakesAssemblies/
261 |
262 | # GhostDoc plugin setting file
263 | *.GhostDoc.xml
264 |
265 | # Node.js Tools for Visual Studio
266 | .ntvs_analysis.dat
267 | node_modules/
268 |
269 | # Visual Studio 6 build log
270 | *.plg
271 |
272 | # Visual Studio 6 workspace options file
273 | *.opt
274 |
275 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
276 | *.vbw
277 |
278 | # Visual Studio LightSwitch build output
279 | **/*.HTMLClient/GeneratedArtifacts
280 | **/*.DesktopClient/GeneratedArtifacts
281 | **/*.DesktopClient/ModelManifest.xml
282 | **/*.Server/GeneratedArtifacts
283 | **/*.Server/ModelManifest.xml
284 | _Pvt_Extensions
285 |
286 | # Paket dependency manager
287 | .paket/paket.exe
288 | paket-files/
289 |
290 | # FAKE - F# Make
291 | .fake/
292 |
293 | # JetBrains Rider
294 | .idea/
295 | *.sln.iml
296 |
297 | # CodeRush
298 | .cr/
299 |
300 | # Python Tools for Visual Studio (PTVS)
301 | __pycache__/
302 | *.pyc
303 |
304 | # Cake - Uncomment if you are using it
305 | # tools/**
306 | # !tools/packages.config
307 |
308 | # Tabs Studio
309 | *.tss
310 |
311 | # Telerik's JustMock configuration file
312 | *.jmconfig
313 |
314 | # BizTalk build output
315 | *.btp.cs
316 | *.btm.cs
317 | *.odx.cs
318 | *.xsd.cs
319 |
320 | # OpenCover UI analysis results
321 | OpenCover/
322 |
323 | # Azure Stream Analytics local run output
324 | ASALocalRun/
325 |
326 | # MSBuild Binary and Structured Log
327 | *.binlog
328 |
329 | # NVidia Nsight GPU debugger configuration file
330 | *.nvuser
331 |
332 | # MFractors (Xamarin productivity tool) working folder
333 | .mfractor/
334 |
335 | # Local History for Visual Studio
336 | .localhistory/
--------------------------------------------------------------------------------
/CCXT/CCXT.pyproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Debug
4 | 2.0
5 | 5d659809-dff8-4d53-8cae-0a66b9e83e46
6 | .
7 | test.py
8 |
9 |
10 | .
11 | .
12 | CCXT
13 | CCXT
14 | Global|PythonCore|3.6-32
15 |
16 |
17 | true
18 | false
19 |
20 |
21 | true
22 | false
23 |
24 |
25 |
26 |
27 |
28 |
29 | Code
30 |
31 |
32 |
33 |
34 |
35 |
36 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/CCXT/CCXTException.py:
--------------------------------------------------------------------------------
1 | class CCXTException:
2 | def __init__(self, exceptionType, message):
3 | self.exceptionType = exceptionType
4 | self.message = message
5 |
6 |
--------------------------------------------------------------------------------
/CCXT/NamedPipes.py:
--------------------------------------------------------------------------------
1 | import struct
2 | #import time
3 | import threading
4 | #import datetime
5 | import platform
6 | import socket
7 |
8 |
9 | class Pipe:
10 |
11 | def __init__(self, pipeNameIn, pipeNameOut):
12 | self.__platform = platform.system()
13 | self.__lock = threading.Lock()
14 |
15 | if self.__platform == 'Linux':
16 | pipeNameIn = '/tmp/CoreFxPipe_' + pipeNameIn
17 | pipeNameOut = '/tmp/CoreFxPipe_' + pipeNameOut
18 | self.__socketIn = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
19 | self.__socketOut = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
20 | try:
21 | self.__socketIn.connect(pipeNameIn)
22 | self.__socketOut.connect(pipeNameOut)
23 | except socket.error as msg:
24 | print(msg)
25 | else:
26 | pipeNameIn = r'\\.\pipe\\' + pipeNameIn
27 | pipeNameOut = r'\\.\pipe\\' + pipeNameOut
28 | self.__pipeIn = open(pipeNameIn, 'r+b', 0)
29 | self.__pipeOut = open(pipeNameOut, 'r+b', 0)
30 |
31 |
32 |
33 | def Read(self):
34 | text = ''
35 | amount_expected = 0
36 | # for some reason c# pipe writes nothing when closes and causes exception in struct
37 | try:
38 | if not (self.__platform == 'Linux'):
39 | #print(threading.current_thread())
40 | self.__lock.acquire()
41 | #print("read started: " + str(time.time()))
42 | amount_expected = struct.unpack('I', self.__pipeIn.read(4))[0] # Read str length
43 | text = self.__pipeIn.read(amount_expected) # Read str
44 | self.__pipeIn.seek(0) # Important!!!
45 | #print("read ended: " + str(time.time()))
46 | self.__lock.release()
47 | #self.start = datetime.datetime.now()
48 | #print(text)
49 | else:
50 | self.__lock.acquire()
51 | amount_expected = struct.unpack('I', self.__socketIn.recv(4))[0]
52 | message = self.__socketIn.recv(amount_expected)
53 | self.__lock.release()
54 | text = message.decode()
55 | except struct.error:
56 | return 'exit'
57 | finally:
58 | if self.__lock.locked():
59 | self.__lock.release()
60 | return text
61 |
62 | def Write(self, text):
63 | if text is None:
64 | return
65 | if not (self.__platform == 'Linux'):
66 | #print("write started: " + str(time.time()))
67 | #print str(datetime.datetime.now()-self.start)
68 | self.__pipeOut.write(struct.pack('I', len(text)) + bytes(text, 'utf-8')) # Write str length and str
69 | self.__pipeOut.seek(0)
70 | #print("write ended: " + str(time.time()))
71 | #print(threading.current_thread())
72 | #print(text)
73 | else:
74 | self.__socketOut.sendall(struct.pack('I', len(text)) + text.encode('utf-8'))
75 |
76 | def Close(self):
77 | if not (self.__platform == 'Linux'):
78 | self.__pipeIn.close()
79 | self.__pipeOut.close()
80 | else:
81 | self.__socketIn.close()
82 | self.__socketOut.close()
83 |
84 |
--------------------------------------------------------------------------------
/CCXT/__init__.py:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/CCXT/ccxtAPI.py:
--------------------------------------------------------------------------------
1 | import json
2 | import sys
3 | import NamedPipes
4 | import CCXTException
5 | import asyncio
6 | import cfscrape
7 | import ccxt.async_support as ccxt
8 | import os
9 | from concurrent.futures import ThreadPoolExecutor
10 |
11 | def run(corofn, *args):
12 | loop = asyncio.new_event_loop()
13 | try:
14 | coro = corofn(*args)
15 | asyncio.set_event_loop(loop)
16 | return loop.run_until_complete(coro)
17 | finally:
18 | loop.close()
19 |
20 | async def Read(pipe):
21 | text = pipe.Read()
22 | if text == "exit":
23 | pipe.Close()
24 | os._exit(0)
25 | return text
26 |
27 | class CCXTAPI:
28 | def __init__(self, pipeNameIn, pipeNameOut):
29 | self.__exchanges = {}
30 | self.__pipe = NamedPipes.Pipe(pipeNameIn, pipeNameOut)
31 |
32 | async def Run(self):
33 | while True:
34 | # reads data from other thread and awaits it
35 | loop = asyncio.get_event_loop()
36 | executor = ThreadPoolExecutor(max_workers=1)
37 | futures = [loop.run_in_executor(executor, run, Read, self.__pipe)]
38 | text = await asyncio.gather(*futures)
39 | try:
40 | data = json.loads(text[0])
41 | # some attributes cannot be awaited so we are switching between async and non async
42 | #if isinstance(getattr(self.__exchanges[data["exchange"]] load_markets, collections.Callable):
43 | if data["handlerType"] is 1:
44 | # calls reciever which calls ccxt method based on read text but doesn't await it so it can read next data that comes through pipe
45 | loop.create_task(self.recieverAsync(data))
46 | else:
47 | self.reciever(data)
48 | except json.JSONDecodeError:
49 | continue
50 | except Exception as ex:
51 | self.__pipe.Write("error" + str(CCXTException.CCXTException(type(ex).__name__, str(ex)).__dict__) + str(data["callNumber"]))
52 | #logger.log(text[0])
53 | print("error")
54 |
55 |
56 |
57 | # gets called when pipe recieves data
58 | def reciever(self, data, retry = False):
59 | response = ""
60 | try:
61 | self.__addExchangeIfRequired(data["exchange"])
62 | # calling method with parameters or not
63 | if data["handlerType"] is 1:
64 | if data["method"]["param1"] is None:
65 | response = getattr(self.__exchanges[data["exchange"]], data["method"]["name"])()
66 | elif data["method"]["param2"] is None:
67 | response = getattr(self.__exchanges[data["exchange"]], data["method"]["name"])(data["method"]["param1"])
68 | elif data["method"]["param3"] is None:
69 | response = getattr(self.__exchanges[data["exchange"]], data["method"]["name"])(data["method"]["param1"], data["method"]["param2"])
70 | elif data["method"]["param4"] is None:
71 | response = getattr(self.__exchanges[data["exchange"]], data["method"]["name"])(data["method"]["param1"], data["method"]["param2"], data["method"]["param3"])
72 | elif data["method"]["param5"] is None:
73 | response = getattr(self.__exchanges[data["exchange"]], data["method"]["name"])(data["method"]["param1"], data["method"]["param2"], data["method"]["param3"], data["method"]["param4"])
74 | elif data["method"]["param6"] is None:
75 | response = getattr(self.__exchanges[data["exchange"]], data["method"]["name"])(data["method"]["param1"], data["method"]["param2"], data["method"]["param3"], data["method"]["param4"], data["method"]["param5"])
76 | elif data["method"]["param7"] is None:
77 | response = getattr(self.__exchanges[data["exchange"]], data["method"]["name"])(data["method"]["param1"], data["method"]["param2"], data["method"]["param3"], data["method"]["param4"], data["method"]["param5"], data["method"]["param6"])
78 | elif data["method"]["param8"] is None:
79 | response = getattr(self.__exchanges[data["exchange"]], data["method"]["name"])(data["method"]["param1"], data["method"]["param2"], data["method"]["param3"], data["method"]["param4"], data["method"]["param5"], data["method"]["param6"], data["method"]["param7"])
80 | elif data["method"]["param9"] is None:
81 | response = getattr(self.__exchanges[data["exchange"]], data["method"]["name"])(data["method"]["param1"], data["method"]["param2"], data["method"]["param3"], data["method"]["param4"], data["method"]["param5"], data["method"]["param6"], data["method"]["param7"], data["method"]["param8"])
82 | else:
83 | response = getattr(self.__exchanges[data["exchange"]], data["method"]["name"])(data["method"]["param1"], data["method"]["param2"], data["method"]["param3"], data["method"]["param4"], data["method"]["param5"], data["method"]["param6"], data["method"]["param7"], data["method"]["param8"], data["method"]["param9"])
84 | else: # setting or getting variable data that is either in exchange or in ccxt api
85 | if data["variable"]["type"] is 1: #set
86 | if data["exchange"] == "ccxt":
87 | setattr(ccxt, data["variable"]["name"], data["variable"]["data"])
88 | else:
89 | setattr(self.__exchanges[data["exchange"]], data["variable"]["name"], data["variable"]["data"])
90 | else:
91 | if data["exchange"] == "ccxt":
92 | response = getattr(ccxt, data["variable"]["name"])
93 | else:
94 | response = getattr(self.__exchanges[data["exchange"]], data["variable"]["name"])
95 | self.__pipe.Write(json.dumps(response) + str(data["callNumber"]))
96 | except ccxt.DDoSProtection as ex:
97 | if not retry:
98 | self.__addDDOSBypass(data["exchange"])
99 | if self.reciever(data, True) == False:
100 | self.__pipe.Write("error" + str(CCXTException.CCXTException(type(ex).__name__, "DDos protection could not be bypassed.").__dict__) + str(data["callNumber"]))
101 | else:
102 | return False
103 | except Exception as ex:
104 | self.__pipe.Write("error" + str(CCXTException.CCXTException(type(ex).__name__, str(ex)).__dict__) + str(data["callNumber"]))
105 | print("error")
106 | return True
107 |
108 | # gets called when pipe recieves data
109 | async def recieverAsync(self, data, retry = False):
110 | response = ""
111 | try:
112 | self.__addExchangeIfRequired(data["exchange"])
113 | # calling method with parameters or not
114 | if data["handlerType"] is 1:
115 | if data["method"]["param1"] is None:
116 | response = await getattr(self.__exchanges[data["exchange"]], data["method"]["name"])()
117 | elif data["method"]["param2"] is None:
118 | response = await getattr(self.__exchanges[data["exchange"]], data["method"]["name"])(data["method"]["param1"])
119 | elif data["method"]["param3"] is None:
120 | response = await getattr(self.__exchanges[data["exchange"]], data["method"]["name"])(data["method"]["param1"], data["method"]["param2"])
121 | elif data["method"]["param4"] is None:
122 | response = await getattr(self.__exchanges[data["exchange"]], data["method"]["name"])(data["method"]["param1"], data["method"]["param2"], data["method"]["param3"])
123 | elif data["method"]["param5"] is None:
124 | response = await getattr(self.__exchanges[data["exchange"]], data["method"]["name"])(data["method"]["param1"], data["method"]["param2"], data["method"]["param3"], data["method"]["param4"])
125 | elif data["method"]["param6"] is None:
126 | response = await getattr(self.__exchanges[data["exchange"]], data["method"]["name"])(data["method"]["param1"], data["method"]["param2"], data["method"]["param3"], data["method"]["param4"], data["method"]["param5"])
127 | elif data["method"]["param7"] is None:
128 | response = await getattr(self.__exchanges[data["exchange"]], data["method"]["name"])(data["method"]["param1"], data["method"]["param2"], data["method"]["param3"], data["method"]["param4"], data["method"]["param5"], data["method"]["param6"])
129 | elif data["method"]["param8"] is None:
130 | response = await getattr(self.__exchanges[data["exchange"]], data["method"]["name"])(data["method"]["param1"], data["method"]["param2"], data["method"]["param3"], data["method"]["param4"], data["method"]["param5"], data["method"]["param6"], data["method"]["param7"])
131 | elif data["method"]["param9"] is None:
132 | response = await getattr(self.__exchanges[data["exchange"]], data["method"]["name"])(data["method"]["param1"], data["method"]["param2"], data["method"]["param3"], data["method"]["param4"], data["method"]["param5"], data["method"]["param6"], data["method"]["param7"], data["method"]["param8"])
133 | else:
134 | response = await getattr(self.__exchanges[data["exchange"]], data["method"]["name"])(data["method"]["param1"], data["method"]["param2"], data["method"]["param3"], data["method"]["param4"], data["method"]["param5"], data["method"]["param6"], data["method"]["param7"], data["method"]["param8"], data["method"]["param9"])
135 | else: # setting or getting variable data that is either in exchange or in ccxt api
136 | if data["variable"]["type"] is 1: #set
137 | if data["exchange"] == "ccxt":
138 | await setattr(ccxt, data["variable"]["name"], data["variable"]["data"])
139 | else:
140 | await setattr(self.__exchanges[data["exchange"]], data["variable"]["name"], data["variable"]["data"])
141 | else:
142 | if data["exchange"] == "ccxt":
143 | response = await getattr(ccxt, data["variable"]["name"]) + str(data["callNumber"])
144 | else:
145 | response = await getattr(self.__exchanges[data["exchange"]], data["variable"]["name"]) + str(data["callNumber"])
146 | #print(json.dumps(response))
147 | self.__pipe.Write(json.dumps(response) + str(data["callNumber"]))
148 | except ccxt.DDoSProtection as ex:
149 | if not retry:
150 | self.__addDDOSBypass(data["exchange"])
151 | if await self.recieverAsync(data, True) == False:
152 | self.__pipe.Write("error" + str(CCXTException.CCXTException(type(ex).__name__, "DDos protection could not be bypassed.").__dict__) + str(data["callNumber"]))
153 | else:
154 | return False
155 | except Exception as ex:
156 | self.__pipe.Write("error" + str(CCXTException.CCXTException(type(ex).__name__, str(ex)).__dict__) + str(data["callNumber"]))
157 | return True
158 |
159 |
160 | def __addDDOSBypass(self, exchangeName):
161 | """
162 | adding async cloudflare scrapper
163 | from aiocfscrape import CloudflareScraper
164 | exchange.session = CloudflareScraper(loop=asyncio.get_event_loop())
165 | """
166 | #bypassing cloudflare with cookies
167 | url = self.__exchanges[exchangeName].urls['www']
168 | tokens, user_agent = cfscrape.get_tokens(url)
169 | self.__exchanges[exchangeName].headers = {
170 | 'cookie': '; '.join([key + '=' + tokens[key] for key in tokens]),
171 | 'user-agent': user_agent,
172 | }
173 |
174 | def __addExchangeIfRequired(self, exchangeName):
175 | if exchangeName not in list(self.__exchanges) and exchangeName != "ccxt":
176 | #assert exchangeName in ccxt.exchanges, "Exchange name is not supported!"
177 | exchange = getattr(ccxt, exchangeName)
178 | self.__exchanges[exchangeName] = exchange()
179 |
180 |
181 | #ccxtAPI = CCXTAPI("ccxtAPICryptoWatcher-PythonPipeOut", "ccxtAPICryptoWatcher-PythonPipeIn")
182 | ccxtAPI = CCXTAPI(sys.argv[1], sys.argv[2])
183 | asyncio.get_event_loop().run_until_complete(ccxtAPI.Run())
184 | print("close")
185 |
186 | """
187 | {
188 | "handlerType": "variable",
189 | "variable":{
190 | "name":"secret" ,
191 | "type": "set"
192 | },
193 | "method":{
194 | "name":"cancel_order",
195 | "param1":1,
196 | "param2":"dd",
197 | "param3":1,
198 | "param4":1,
199 | "param5":1,
200 | "param6":1,
201 | "param7":1,
202 | "param8":1,
203 | "param9":1
204 | },
205 | "exchange":"binance"
206 | }
207 | """
--------------------------------------------------------------------------------
/CCXT/ccxtAPI.spec:
--------------------------------------------------------------------------------
1 | # -*- mode: python -*-
2 |
3 | block_cipher = None
4 |
5 |
6 | a = Analysis(['ccxtAPI.py'],
7 | pathex=['D:\\Documents\\New folder\\CCXTSharp\\CCXT'],
8 | binaries=[],
9 | datas=[],
10 | hiddenimports=[],
11 | hookspath=[],
12 | runtime_hooks=[],
13 | excludes=[],
14 | win_no_prefer_redirects=False,
15 | win_private_assemblies=False,
16 | cipher=block_cipher,
17 | noarchive=False)
18 | pyz = PYZ(a.pure, a.zipped_data,
19 | cipher=block_cipher)
20 | exe = EXE(pyz,
21 | a.scripts,
22 | a.binaries,
23 | a.zipfiles,
24 | a.datas,
25 | [],
26 | name='ccxtAPI',
27 | debug=False,
28 | bootloader_ignore_signals=False,
29 | strip=False,
30 | upx=True,
31 | runtime_tmpdir=None,
32 | console=False )
33 |
--------------------------------------------------------------------------------
/CCXT/test.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 | import ccxt
3 | import ccxt.async_support as ccxtasync
4 |
5 |
6 | #i = 0
7 | #async def func():
8 | # for e in ccxtasync.exchanges:
9 | # exchange = getattr(ccxtasync,e)()
10 | # try:
11 | # has = exchange.has
12 | # except :
13 | # print(e)
14 | # finally:
15 | # await exchange.close()
16 |
17 |
18 | #asyncio.get_event_loop().run_until_complete(func())
19 |
20 | #print(str(getattr(ccxt,e)().enableRateLimit))
21 | # i+=1
22 |
23 | #print(i)
24 |
25 | #ExchangeNotAvailable('zb {"result":false,"message":"\\u670d\\u52a1\\u7aef\\u5fd9\\u788c"}',)
26 |
27 |
28 | #e = ccxt.theocean()
29 | #print(e.name)
30 | e = ccxt.zb()
31 | e.verbose = True
32 | markets = e.fetch_markets()
33 | print(e.has)
34 | for market in markets:
35 | print(e.fetch_ticker(market["symbol"]))
36 | print("done")
37 | input()
38 |
39 | '''
40 |
41 | import ccxt
42 | import time
43 |
44 | e = ccxt.luno()
45 | e.enableRateLimit = True
46 |
47 |
48 | for i in range(100):
49 | t = time.time()
50 | print(e.fetchTicker("BTC/EUR"))
51 | print("elapsed:" + str(time.time()-t))
52 |
53 | input()
54 |
55 | '''
--------------------------------------------------------------------------------
/CCXTSharp.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.28010.0
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{888888A0-9F3D-457C-B088-3A5042F75D52}") = "CCXT", "CCXT\CCXT.pyproj", "{5D659809-DFF8-4D53-8CAE-0A66B9E83E46}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example", "Example\Example.csproj", "{4FCF4BB6-A561-441D-A8CA-3B0B897FB313}"
9 | EndProject
10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CCXTSharp", "CCXTSharp\CCXTSharp.csproj", "{52849576-D600-4266-9FE9-F7C87D4C2AC3}"
11 | EndProject
12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CCXTSharpTest", "CCXTSharpTest\CCXTSharpTest.csproj", "{FAAE9351-367B-4DBF-B09A-88F1E9143365}"
13 | EndProject
14 | Global
15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
16 | Debug|Any CPU = Debug|Any CPU
17 | Release|Any CPU = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
20 | {5D659809-DFF8-4D53-8CAE-0A66B9E83E46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {5D659809-DFF8-4D53-8CAE-0A66B9E83E46}.Release|Any CPU.ActiveCfg = Release|Any CPU
22 | {4FCF4BB6-A561-441D-A8CA-3B0B897FB313}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
23 | {4FCF4BB6-A561-441D-A8CA-3B0B897FB313}.Debug|Any CPU.Build.0 = Debug|Any CPU
24 | {4FCF4BB6-A561-441D-A8CA-3B0B897FB313}.Release|Any CPU.ActiveCfg = Release|Any CPU
25 | {4FCF4BB6-A561-441D-A8CA-3B0B897FB313}.Release|Any CPU.Build.0 = Release|Any CPU
26 | {52849576-D600-4266-9FE9-F7C87D4C2AC3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27 | {52849576-D600-4266-9FE9-F7C87D4C2AC3}.Debug|Any CPU.Build.0 = Debug|Any CPU
28 | {52849576-D600-4266-9FE9-F7C87D4C2AC3}.Release|Any CPU.ActiveCfg = Release|Any CPU
29 | {52849576-D600-4266-9FE9-F7C87D4C2AC3}.Release|Any CPU.Build.0 = Release|Any CPU
30 | {FAAE9351-367B-4DBF-B09A-88F1E9143365}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
31 | {FAAE9351-367B-4DBF-B09A-88F1E9143365}.Debug|Any CPU.Build.0 = Debug|Any CPU
32 | {FAAE9351-367B-4DBF-B09A-88F1E9143365}.Release|Any CPU.ActiveCfg = Release|Any CPU
33 | {FAAE9351-367B-4DBF-B09A-88F1E9143365}.Release|Any CPU.Build.0 = Release|Any CPU
34 | EndGlobalSection
35 | GlobalSection(SolutionProperties) = preSolution
36 | HideSolutionNode = FALSE
37 | EndGlobalSection
38 | GlobalSection(ExtensibilityGlobals) = postSolution
39 | SolutionGuid = {85BEAEA8-5C1B-4ACF-899A-AB5C41C98E68}
40 | EndGlobalSection
41 | EndGlobal
42 |
--------------------------------------------------------------------------------
/CCXTSharp/Balances.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json.Linq;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace CCXTSharp
9 | {
10 | public class Balances
11 | {
12 | public Dictionary balances { get; set; } = new Dictionary();
13 | public Dictionary free { get; set; } = new Dictionary();
14 | public Dictionary used { get; set; } = new Dictionary();
15 | public Dictionary total { get; set; } = new Dictionary();
16 | public Dictionary info { get; set; } = new Dictionary();
17 |
18 | public Balances(string json)
19 | {
20 | JObject jObject = JObject.Parse(json);
21 |
22 | foreach (var obj in jObject)
23 | {
24 | if (obj.Key == "info")
25 | {
26 | info = obj.Value.ToObject>();
27 | }
28 | else if (obj.Key == "free")
29 | {
30 | foreach (var symbol in obj.Value.Children())
31 | {
32 | JProperty jProperty = symbol.ToObject();
33 | free.Add(jProperty.Name, jProperty.Value.ToObject());
34 | }
35 | }
36 | else if (obj.Key == "used")
37 | {
38 | foreach (var symbol in obj.Value.Children())
39 | {
40 | JProperty jProperty = symbol.ToObject();
41 | used.Add(jProperty.Name, jProperty.Value.ToObject());
42 | }
43 | }
44 | else if (obj.Key == "total")
45 | {
46 | foreach (var symbol in obj.Value.Children())
47 | {
48 | JProperty jProperty = symbol.ToObject();
49 | total.Add(jProperty.Name, jProperty.Value.ToObject());
50 | }
51 | }
52 | else
53 | {
54 | balances.Add(obj.Key, obj.Value.ToObject());
55 | }
56 | }
57 | }
58 |
59 | public class Balance
60 | {
61 | public float? free { get; set; }
62 | public float? used { get; set; }
63 | public float? total { get; set; }
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/CCXTSharp/CCXTException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace CCXTSharp
8 | {
9 | public class CCXTException : Exception
10 | {
11 | public ExceptionType? exceptionType { get; set; } = null;
12 | public string OtherType { get; set; }
13 | public bool HandledByCCXT { get; set; } = true;
14 | public enum ExceptionType
15 | {
16 | BaseError,
17 | ExchangeError,
18 | NotSupported,
19 | AuthenticationError,
20 | PermissionDenied,
21 | InsufficientFunds,
22 | InvalidAddress,
23 | InvalidOrder,
24 | OrderNotFound,
25 | NetworkError,
26 | DDoSProtection,
27 | RequestTimeout,
28 | ExchangeNotAvailable,
29 | InvalidNonce
30 | }
31 |
32 | public CCXTException(string message, string type) : base(message)
33 | {
34 | if(Enum.GetNames(typeof(ExceptionType)).Any((name) => { if (name == type) return true; else return false; }))
35 | exceptionType = (ExceptionType)Enum.Parse(typeof(ExceptionType), type);
36 | else
37 | {
38 | HandledByCCXT = false;
39 | OtherType = type;
40 | }
41 | HelpLink = "https://github.com/ccxt/ccxt/wiki/Manual#error-handling";
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/CCXTSharp/CCXTSharp.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {52849576-D600-4266-9FE9-F7C87D4C2AC3}
8 | Library
9 | Properties
10 | CCXTSharp
11 | CCXTSharp
12 | v4.5
13 | 512
14 | true
15 |
16 |
17 |
18 | true
19 | full
20 | false
21 | bin\Debug\
22 | DEBUG;TRACE
23 | prompt
24 | 4
25 | false
26 | bin\Debug\CCXTSharp.xml
27 |
28 |
29 | pdbonly
30 | true
31 | bin\Release\
32 | TRACE
33 | prompt
34 | 4
35 | false
36 |
37 |
38 |
39 | ..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/CCXTSharp/CCXTSharp.nuspec:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | CCXTSharp
5 | 1.2.0
6 | A cryptocurrency trading library with support for more than 100 bitcoin/altcoin exchanges. Uses original ccxt (python) code. https://github.com/ccxt/ccxt
7 | CCXTSharp
8 | Stock84-dev
9 | https://raw.githubusercontent.com/Stock84-dev/CCXTSharp/master/LICENSE
10 | https://github.com/Stock84-dev/CCXTSharp
11 | false
12 | Added support for Linux. Fixed json parsing in FetchOHLCV method. Updated ccxt to v1.18.322.
13 | Copyright 2018-2019
14 | altcoin api arbitrage bitcoin bot cryptocurrency crypto e-commerce ethereum exchange invest library strategy trading btc eth trade merchant market-data exchange-api trading-api exchange-markets ccxt
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/CCXTSharp/CcxtAPI.Message.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Reflection;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace CCXTSharp
9 | {
10 | partial class CcxtAPI
11 | {
12 | private class Message
13 | {
14 | public int callNumber { get; set; }
15 | public int handlerType { get; set; }
16 | public string exchange { get; set; }
17 | public Variable variable { get; set; }
18 | public Method method { get; set; }
19 |
20 | public Message(string name, string parentName, int callNumber, object setVariableData = null, bool isMethod = true, params object[] methodParameters)
21 | {
22 | // creating message for method
23 | if (isMethod)
24 | {
25 | handlerType = 1;
26 | method = new Method();
27 | method.name = name;
28 | PropertyInfo[] properties = method.GetType().GetProperties();
29 | for (int i = 0; i < methodParameters.Length; i++)
30 | {
31 | properties[i + 1].SetValue(method, methodParameters[i]);
32 | }
33 | }
34 | else // creating message for variable
35 | {
36 | handlerType = 0;
37 | variable = new Variable();
38 | variable.name = name;
39 | if (setVariableData != null)
40 | {
41 | variable.type = 1;
42 | variable.data = setVariableData;
43 | }
44 | else
45 | variable.type = 0;
46 | }
47 |
48 | this.exchange = parentName;
49 | this.callNumber = callNumber;
50 | }
51 |
52 | public class Variable
53 | {
54 | public string name { get; set; }
55 | public int type { get; set; }
56 | public object data { get; set; } = null;
57 | }
58 |
59 | public class Method
60 | {
61 | //NOTE: order of properties is important
62 | public string name { get; set; }
63 | public object param1 { get; set; } = null;
64 | public object param2 { get; set; } = null;
65 | public object param3 { get; set; } = null;
66 | public object param4 { get; set; } = null;
67 | public object param5 { get; set; } = null;
68 | public object param6 { get; set; } = null;
69 | public object param7 { get; set; } = null;
70 | public object param8 { get; set; } = null;
71 | public object param9 { get; set; } = null;
72 | }
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/CCXTSharp/CcxtAPI.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.IO;
5 | using System.IO.Pipes;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Threading;
9 | using System.Threading.Tasks;
10 | using Newtonsoft.Json;
11 | using Newtonsoft.Json.Linq;
12 | using System.Reflection;
13 |
14 | // TODO: make wrapper if user skipped to define parameter e.g. FetchTrades("binance", "BTC/USDT", limit = 10), it will call function without limit, because parameter before limit is null
15 | namespace CCXTSharp
16 | {
17 | public enum Timeframe { NONE, min1 = 60, min3 = 180, min5 = 300, min15 = 900, min30 = 1800, h1 = 3600, h2 = 7200, h4 = 14400, h6 = 21600, h8 = 28800, h12 = 43200, d1 = 86400, d3 = 259200, w1 = 604800, M1 = 2419200 }
18 |
19 | public partial class CcxtAPI
20 | {
21 | private readonly string PIPE_NAME_IN, PIPE_NAME_OUT;
22 | private readonly object _msgDataLock = new object();
23 | private Process _ccxtAPIProcess;
24 | private Dictionary _msgData = new Dictionary();
25 | private NamedPipe _namedPipe;
26 | private Dictionary _rateLimits = new Dictionary()
27 | {
28 | // = not provided, ds = different system
29 | { "_1broker", 1500 }, // ds
30 | { "_1btcxe", 2000 }, // exchange down
31 | { "acx", 50 },
32 | { "allcoin", 1000 }, //
33 | { "anxpro", 200 },
34 | { "anybits", 2000 }, //
35 | { "bcex", 2000 }, //
36 | { "bibox", 2000 }, //
37 | { "bigone", 10 },
38 | { "binance", 864 }, // ds
39 | { "bit2c", 3000 }, //
40 | { "bitbay", 1000 }, //
41 | { "bitfinex", 1500 }, // ds
42 | { "bitfinex2", 1500 }, // ds
43 | };
44 |
45 |
46 | ///
47 | /// Starts new process with ccxt API.
48 | ///
49 | /// Path to script or compiled .exe.
50 | /// Python 3.5+ supported.
51 | /// Show python console if you are using python interpreter.
52 | /// Rename if you run multiple instances.
53 | /// Rename if you run multiple instances.
54 | public CcxtAPI(string pathToCcxtAPIScriptOrExe, string pathToPythonExe = null, bool showPythonConsole = false, string pipeNameIn = "CCXTSharp-PipeIn", string pipeNameOut = "CCXTSharp-PipeOut")
55 | {
56 | bool usePythonInterpreter = pathToPythonExe != null ? true : false;
57 |
58 | if (!usePythonInterpreter && showPythonConsole)
59 | throw new ArgumentException("Cannot show console if python interpreter isn't used.");
60 |
61 | if (pathToCcxtAPIScriptOrExe.EndsWith(".py") && !usePythonInterpreter)
62 | throw new ArgumentException("Cannot use script without python interpreter.");
63 |
64 | if (pathToCcxtAPIScriptOrExe.EndsWith(".exe") && usePythonInterpreter)
65 | throw new ArgumentException("Cannot use python interpreter on executable.");
66 |
67 | PIPE_NAME_IN = pipeNameIn;
68 | PIPE_NAME_OUT = pipeNameOut;
69 | ProcessStartInfo myProcessStartInfo;
70 |
71 | if (usePythonInterpreter)
72 | {
73 | myProcessStartInfo = new ProcessStartInfo(pathToPythonExe);
74 | // "-i " in front of script path to freze window after exception
75 | if (showPythonConsole)
76 | pathToCcxtAPIScriptOrExe = "-i \"" + pathToCcxtAPIScriptOrExe + "\"";
77 | else
78 | {
79 | myProcessStartInfo.UseShellExecute = false;
80 | myProcessStartInfo.CreateNoWindow = true;
81 | pathToCcxtAPIScriptOrExe = "\"" + pathToCcxtAPIScriptOrExe + "\"";
82 | }
83 | myProcessStartInfo.Arguments = pathToCcxtAPIScriptOrExe + " " + PIPE_NAME_OUT + " " + PIPE_NAME_IN;
84 | }
85 | else
86 | {
87 | pathToCcxtAPIScriptOrExe = "\"" + pathToCcxtAPIScriptOrExe + "\"";
88 | myProcessStartInfo = new ProcessStartInfo(pathToCcxtAPIScriptOrExe);
89 | myProcessStartInfo.Arguments = PIPE_NAME_OUT + " " + PIPE_NAME_IN;
90 | }
91 |
92 | _ccxtAPIProcess = new Process();
93 | // assign start information to the process
94 | _ccxtAPIProcess.StartInfo = myProcessStartInfo;
95 |
96 | // start the process
97 | _ccxtAPIProcess.Start();
98 |
99 | // Open the named pipe.
100 | _namedPipe = new NamedPipe(PIPE_NAME_IN, PIPE_NAME_OUT);
101 | _namedPipe.OnMessage += OnMessage;
102 | }
103 |
104 | ///
105 | /// Show communication messages between programs in console.
106 | ///
107 | public bool ShowPipeData { get; set; } = false;
108 |
109 | ///
110 | /// Closes communication between python ccxt.
111 | ///
112 | public async Task Close()
113 | {
114 | await _namedPipe.Close();
115 |
116 | lock (_msgDataLock)
117 | {
118 | foreach (var msg in _msgData.Values)
119 | {
120 | Task.Run(() => msg.TaskCompleted.SetResult(false));
121 | }
122 | }
123 | }
124 | ///
125 | /// Use this only if python program breaks.
126 | ///
127 | public void Kill()
128 | {
129 | _ccxtAPIProcess.Kill();
130 | }
131 |
132 | ///
133 | /// Returns a list of exchange ids.
134 | ///
135 | public async Task> GetExchangeIds()
136 | {
137 | return await GetData>("exchanges", "ccxt", null, false);
138 | }
139 |
140 | #region Exchange properties
141 | public async Task GetExchangeName(string exchnageId)
142 | {
143 | return await GetData("name", exchnageId, null, false);
144 | }
145 |
146 | public async Task> GetExchangeCountries(string exchnageId)
147 | {
148 | return await GetData>("countries", exchnageId, null, false);
149 | }
150 |
151 | public async Task GetExchangeHas(string exchnageId)
152 | {
153 | var data = await GetData>("has", exchnageId, null, false);
154 | Has has = new Has();
155 | foreach (var property in has.GetType().GetProperties())
156 | {
157 | foreach (var capability in data)
158 | {
159 | if (capability.Key == property.Name)
160 | {
161 | if (capability.Value.GetType() == typeof(string))
162 | property.SetValue(has, Has.Capability.Emulated);
163 | else if ((bool)capability.Value == true)
164 | property.SetValue(has, Has.Capability.True);
165 | else
166 | property.SetValue(has, Has.Capability.False);
167 | }
168 | }
169 | }
170 | return has;
171 | }
172 |
173 | public async Task> GetExchangeTimeframes(string exchnageId)
174 | {
175 | return await GetData>("timeframes", exchnageId, null, false);
176 | }
177 |
178 | public async Task ExchangeTimeout(string exchnageId, int? timeout = null)
179 | {
180 | return await GetData("timeout", exchnageId, timeout, false);
181 | }
182 |
183 | public async Task ExchangeRateLimit(string exchnageId, int? rateLimit = null)
184 | {
185 | return await GetData("rateLimit", exchnageId, rateLimit, false);
186 | }
187 |
188 | public async Task ExchangeVerbose(string exchnageId, bool? verbose = null)
189 | {
190 | return await GetData("verbose", exchnageId, verbose, false);
191 | }
192 |
193 | public async Task> GetExchangeMarkets(string exchnageId)
194 | {
195 | return await GetData>("markets", exchnageId, null, false);
196 | }
197 |
198 | ///
199 | /// Returns orders cached by ccxt.
200 | ///
201 | public async Task> GetExchangeOrders(string exchnageId)
202 | {
203 | return await GetData>("orders", exchnageId, null, false);
204 | }
205 |
206 | public async Task> GetExchangeSymbols(string exchnageId)
207 | {
208 | return await GetData>("symbols", exchnageId, null, false);
209 | }
210 |
211 | public async Task> GetExchangeCurrencies(string exchnageId)
212 | {
213 | return await GetData>("currencies", exchnageId, null, false);
214 | }
215 |
216 | public async Task> GetExchangeMarketsById(string exchnageId)
217 | {
218 | return await GetData>("markets_by_id", exchnageId, null, false);
219 | }
220 |
221 | public async Task ExchangeProxy(string exchnageId, string proxy = null)
222 | {
223 | return await GetData("proxy", exchnageId, proxy, false);
224 | }
225 |
226 | public async Task ExchangeApiKey(string exchnageId, string apiKey = null)
227 | {
228 | return await GetData("apiKey", exchnageId, apiKey, false);
229 | }
230 |
231 | public async Task ExchangeSecret(string exchnageId, string secret = null)
232 | {
233 | return await GetData("secret", exchnageId, secret, false);
234 | }
235 |
236 | public async Task ExchangePassword(string exchnageId, string password = null)
237 | {
238 | return await GetData("password", exchnageId, password, false);
239 | }
240 |
241 | public async Task ExchangeUserId(string exchnageId, string uId = null)
242 | {
243 | return await GetData("uid", exchnageId, uId, false);
244 | }
245 |
246 | public async Task ExchangeEnableRateLimit(string exchnageId, bool? enambeRateLimit = null)
247 | {
248 | return await GetData("rateLimit", exchnageId, enambeRateLimit, false);
249 | }
250 |
251 | ///
252 | /// Clears .orders cache before millisecond timestamp.
253 | ///
254 | public async Task ExchangePurgeCachedOrders(string exchnageId, long before)
255 | {
256 | return await GetData("purgeCachedOrders", exchnageId, null, true, -1, false, before);
257 | }
258 |
259 | #endregion
260 |
261 | #region Public API
262 | public async Task> LoadMarkets(string exchange, bool reloadCache = false)
263 | {
264 | return await GetData>("load_markets", exchange, methodParameters: reloadCache);
265 | }
266 |
267 | public async Task> FetchMarkets(string exchangeId)
268 | {
269 | return await GetData>("fetch_markets", exchangeId);
270 | }
271 |
272 | ///
273 | /// Returns empty list if load markets hasn't been called.
274 | ///
275 | public async Task> FetchCurrencies(string exchangeId)
276 | {
277 | return await GetData>("fetch_currencies", exchangeId);
278 | }
279 |
280 | public async Task FetchTicker(string exchangeId, string symbol)
281 | {
282 | return await GetData("fetch_ticker", exchangeId, methodParameters: symbol);
283 | }
284 |
285 | ///
286 | /// Some exchanges doesn't support to fetch all tickers.
287 | ///
288 | public async Task> FetchTickers(string exchangeId)
289 | {
290 | return await GetData>("fetch_tickers", exchangeId);
291 | }
292 |
293 | ///
294 | /// Parameters are exchange specific and aren't unified.
295 | ///
296 | public async Task FetchOrderBook(string exchangeId, string symbol, int? limit = null, Dictionary parameters = null)
297 | {
298 | return new OrderBook(await GetData("fetch_order_book", exchangeId, null, true, -1, false, symbol, limit, parameters));
299 | }
300 |
301 | ///
302 | /// Returns aggregated orderbook. Parameters are exchange specific and aren't unified.
303 | ///
304 | public async Task FetchL2OrderBook(string exchangeId, string symbol, int? limit = null, Dictionary parameters = null)
305 | {
306 | return new OrderBook(await GetData("fetchL2OrderBook", exchangeId, null, true, -1, false, symbol, limit, parameters));
307 | }
308 |
309 | public async Task> FetchOHLCV(string exchangeId, string symbol, Timeframe? timeframe = null, long? since = null, int? limit = null, Dictionary parameters = null)
310 | {
311 | string timeframeKey = timeframe != null ? TimeframeToKey(timeframe.Value) : null;
312 | return await FetchOHLCV(exchangeId, symbol, timeframeKey, since, limit, parameters);
313 |
314 | }
315 |
316 | public async Task> FetchOHLCV(string exchangeId, string symbol, string timeframe = null, long? since = null, int? limit = null, Dictionary parameters = null)
317 | {
318 | string text = await GetData("fetchOHLCV", exchangeId, null, true, -1, false, symbol, timeframe, since, limit, parameters);
319 | var response = JArray.Parse(text);
320 | return (from responseCandle in response
321 | select new Candlestick(responseCandle[0].ToObject(), responseCandle[1].ToObject(), responseCandle[2].ToObject(), responseCandle[3].ToObject(), responseCandle[4].ToObject(), responseCandle[5].ToObject())).ToList();
322 |
323 | }
324 |
325 | public async Task> FetchTrades(string exchangeId, string symbol, long? since = null, int? limit = null, Dictionary parameters = null)
326 | {
327 | return await GetData>("fetch_trades", exchangeId, null, true, -1, true, symbol, since, limit, parameters);
328 | }
329 |
330 | #endregion
331 |
332 | #region Private API
333 | public async Task FetchBalance(string exchangeId)
334 | {
335 | return new Balances(await GetData("fetch_balance", exchangeId, null, true, -1, false));
336 | }
337 |
338 | public async Task CreateOrder(string exchangeId, string symbol, OrderType type, OrderSide side, float amount, float price, Dictionary parameters = null)
339 | {
340 | return await GetData("create_order", exchangeId, null, true, -1, true, symbol, type.ToString(), side.ToString(), amount, price, parameters);
341 | }
342 |
343 | public async Task CancelOrder(string exchangeId, string id, string symbol, Dictionary parameters = null)
344 | {
345 | return await GetData("cancel_order", exchangeId, null, true, -1, true, id, symbol, parameters);
346 | }
347 |
348 | public async Task FetchOrder(string exchangeId, string id, string symbol, Dictionary parameters = null)
349 | {
350 | return await GetData("fetch_order", exchangeId, null, true, -1, true, id, symbol, parameters);
351 | }
352 |
353 | ///
354 | /// Fetching orders without specifying a symbol is rate-limited.
355 | ///
356 | public async Task> FetchOrders(string exchangeId, string symbol = null, long? since = null, int? limit = null, Dictionary parameters = null)
357 | {
358 | return await GetData>("fetch_orders", exchangeId, null, true, -1, true, symbol, since, limit, parameters);
359 | }
360 |
361 | ///
362 | /// Fetching orders without specifying a symbol is rate-limited.
363 | ///
364 | public async Task> FetchOpenOrders(string exchangeId, string symbol = null, long? since = null, int? limit = null, Dictionary parameters = null)
365 | {
366 | return await GetData>("fetchOpenOrders", exchangeId, null, true, -1, true, symbol, since, limit, parameters);
367 | }
368 |
369 | ///
370 | /// Fetching orders without specifying a symbol is rate-limited.
371 | ///
372 | public async Task> FetchClosedOrders(string exchangeId, string symbol = null, long? since = null, int? limit = null, Dictionary parameters = null)
373 | {
374 | return await GetData>("fetchClosedOrders", exchangeId, null, true, -1, true, symbol, since, limit, parameters);
375 | }
376 |
377 | ///
378 | /// Fetching orders without specifying a symbol is rate-limited.
379 | ///
380 | public async Task> FetchMyTrades(string exchangeId, string symbol, long? since = null, int? limit = null, Dictionary parameters = null)
381 | {
382 | return await GetData>("fetchMyTrades", exchangeId, null, true, -1, true, symbol, since, limit, parameters);
383 | }
384 |
385 | /// Can be found in Market.Base
386 | public async Task FetchDepositAddress(string exchangeId, string baseAsset, Dictionary parameters = null)
387 | {
388 | return await GetData("fetchDepositAddress", exchangeId, null, true, -1, true, baseAsset, parameters);
389 | }
390 |
391 | public async Task Withdraw(string exchangeId, string baseAsset, float amount, string address, string tag = null, Dictionary parameters = null)
392 | {
393 | return await GetData("withdraw", exchangeId, null, true, -1, true, baseAsset, amount, address, tag, parameters);
394 | }
395 |
396 | public async Task> FetchDeposits(string exchangeId, string baseAsset = null, long? since = null, int? limit = null, Dictionary parameters = null)
397 | {
398 | return await GetData>("fetchDeposits", exchangeId, null, true, -1, true, baseAsset, parameters);
399 | }
400 |
401 | public async Task> FetchWithdrawals(string exchangeId, string baseAsset = null, long? since = null, int? limit = null, Dictionary parameters = null)
402 | {
403 | return await GetData>("fetchWithdrawals", exchangeId, null, true, -1, true, baseAsset, parameters);
404 | }
405 |
406 | public async Task> FetchTransactions(string exchangeId, string baseAsset = null, long? since = null, int? limit = null, Dictionary parameters = null)
407 | {
408 | return await GetData>("fetchTransactions", exchangeId, null, true, -1, true, baseAsset, parameters);
409 | }
410 | #endregion
411 |
412 | ///
413 | /// Gets wait time in ms after each call it's less restictive than original ccxt raleLimit.
414 | ///
415 | //public Dictionary RateLimits { get { return _rateLimits; } }
416 |
417 | public static string TimeframeToKey(Timeframe timeframe)
418 | {
419 | switch (timeframe)
420 | {
421 | case Timeframe.min1: return "1m";
422 | case Timeframe.min5: return "5m";
423 | case Timeframe.min15: return "15m";
424 | case Timeframe.min30: return "30m";
425 | case Timeframe.h1: return "1h";
426 | case Timeframe.h2: return "2h";
427 | case Timeframe.h4: return "4h";
428 | case Timeframe.h6: return "6h";
429 | case Timeframe.h8: return "8h";
430 | case Timeframe.h12: return "12h";
431 | case Timeframe.d1: return "1d";
432 | case Timeframe.d3: return "3d";
433 | case Timeframe.w1: return "1w";
434 | case Timeframe.M1: return "1M";
435 | }
436 | throw new ArgumentException();
437 | }
438 |
439 | ///
440 | /// Use this to request data from ccxt that isn't implemented in c#.
441 | ///
442 | /// Message is automatically deserialized to desired object.
443 | /// Name of a method, variable to call.
444 | /// Name of an object that contains method or variable to call.
445 | /// Used for awaiting asynchronous code, You probably don't need this.
446 | /// Deserialized object.
447 | public async Task GetData(string name, string parentName, object setVariableData = null, bool isMethod = true, int callNumber = -1, bool deserialize = true, params object[] methodParameters)
448 | {
449 | if (callNumber == -1)
450 | callNumber = AddMessageToQueue();
451 | Message msg = new Message(name, parentName, callNumber, setVariableData, isMethod, methodParameters);
452 | await _namedPipe.Write(JsonConvert.SerializeObject(msg));
453 | if (deserialize)
454 | return JsonConvert.DeserializeObject(CheckForError(await GetMessageData(msg.callNumber)));
455 | else
456 | return (T)Convert.ChangeType(CheckForError(await GetMessageData(msg.callNumber)), typeof(T));
457 | }
458 |
459 | ///
460 | /// Throws CCXTException if error is found, else returns message.
461 | ///
462 | private string CheckForError(MessageData messageData)
463 | {
464 | if (_ccxtAPIProcess.HasExited)
465 | throw new CCXTException("Trying to write while CCXTAPI is closed.", "ComunicationClosed");
466 | if (messageData.IsError)
467 | {
468 | var e = JsonConvert.DeserializeObject(messageData.Msg);
469 | throw new CCXTException(e.message, e.exceptionType);
470 | }
471 | else return messageData.Msg;
472 | }
473 |
474 | private async Task GetMessageData(int callNumber)
475 | {
476 | MessageData msgData;
477 | await _msgData[callNumber].TaskCompleted.Task;
478 | lock (_msgDataLock)
479 | {
480 | msgData = new MessageData(_msgData[callNumber].Msg, _msgData[callNumber].TaskCompleted, _msgData[callNumber].IsError);
481 | _msgData.Remove(callNumber);
482 | }
483 | return msgData;
484 | }
485 |
486 | ///
487 | /// Adds message to _msgData dictionary and returns key(call number).
488 | ///
489 | private int AddMessageToQueue()
490 | {
491 | lock (_msgDataLock)
492 | {
493 | for (int i = 0; i < int.MaxValue; i++)
494 | {
495 | if (!_msgData.ContainsKey(i))
496 | {
497 | _msgData.Add(i, new MessageData());
498 | return i;
499 | }
500 | }
501 | }
502 | throw new Exception("Not enough space in queue.");
503 | }
504 |
505 | private void OnMessage(object sender, NamedPipe.OnMessageEventArgs e)
506 | {
507 | if (ShowPipeData)
508 | {
509 | Console.WriteLine(e.Message);
510 | }
511 | int i;
512 | for (i = e.Message.Length - 1; i >= 0; i--)
513 | {
514 | if (!(e.Message[i] >= '0' && e.Message[i] <= '9'))
515 | {
516 | i++;
517 | break;
518 | }
519 | }
520 | int callNumber = int.Parse(e.Message.Substring(i));
521 | // if error occurred
522 | lock (_msgDataLock)
523 | {
524 | if (e.Message.IndexOf('{') != 0 && e.Message.IndexOf('[') != 0 && e.Message.IndexOf('\"') != 0)
525 | {
526 | _msgData[callNumber].Msg = e.Message.Substring(5, i - 5);
527 | _msgData[callNumber].IsError = true;
528 | }
529 | else _msgData[callNumber].Msg = e.Message.Substring(0, i);
530 | }
531 | _msgData[callNumber].TaskCompleted.SetResult(true);
532 | }
533 |
534 | private class MessageData
535 | {
536 | public MessageData() { }
537 |
538 | public MessageData(string msg, TaskCompletionSource taskCompleted, bool isError)
539 | {
540 | Msg = msg;
541 | TaskCompleted = taskCompleted;
542 | IsError = isError;
543 | }
544 |
545 | public string Msg { get; set; } = null;
546 | public TaskCompletionSource TaskCompleted { get; set; } = new TaskCompletionSource();
547 | public bool IsError { get; set; } = false;
548 | }
549 |
550 | private struct ExceptionMessage
551 | {
552 | public string exceptionType { get; set; }
553 | public string message { get; set; }
554 | }
555 | }
556 |
557 | public class Transaction
558 | {
559 | public string id { get; set; }
560 | public string txid { get; set; }
561 | public long? timestamp { get; set; }
562 | public DateTime? datetime { get; set; }
563 | public string address { get; set; }
564 | public string type { get; set; }
565 | public TransactionType transactionType { get { return (TransactionType)Enum.Parse(typeof(TransactionType), type); } }
566 | public float? amount { get; set; }
567 | public string currency { get; set; }
568 | public string status { get; set; }
569 | public TransactionStatus transactionStatus { get { return (TransactionStatus)Enum.Parse(typeof(TransactionStatus), status); } }
570 | public long? updated { get; set; }
571 | public Fee fee { get; set; }
572 |
573 | [JsonExtensionData]
574 | public Dictionary info { get; set; }
575 |
576 | public enum TransactionType { deposit, withdrawal }
577 | public enum TransactionStatus { ok, failed, canceled }
578 | }
579 |
580 | public class Address
581 | {
582 | public string currency { get; set; }
583 | public string address { get; set; }
584 | public string tag { get; set; }
585 | [JsonExtensionData]
586 | public Dictionary info { get; set; }
587 | }
588 |
589 | public class Withdrawal
590 | {
591 | public string id { get; set; }
592 | [JsonExtensionData]
593 | public Dictionary info { get; set; }
594 | }
595 |
596 | public enum OrderSide { buy, sell }
597 | public enum OrderType { limit, market }
598 | public enum OrderStatus { open, closed, canceled }
599 |
600 | public class Order
601 | {
602 | public string id { get; set; }
603 | public long? timestamp { get; set; }
604 | public DateTime? datetime { get; set; }
605 | public long? lastTradeTimestamp { get; set; }
606 | public string symbol { get; set; }
607 | public string type { get; set; }
608 | public string side { get; set; }
609 | public float? price { get; set; }
610 | public float? amount { get; set; }
611 | public float? cost { get; set; }
612 | public float? filled { get; set; }
613 | public float? remaining { get; set; }
614 | public string status { get; set; }
615 | public OrderStatus OrderStatus { get { return (OrderStatus)Enum.Parse(typeof(OrderStatus), status); } }
616 | public Fee fee { get; set; }
617 | //TODO: unknown type
618 | public List