├── .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 trades { get; set; } 619 | [JsonExtensionData] 620 | public Dictionary info { get; set; } 621 | } 622 | 623 | public class Candlestick 624 | { 625 | [JsonProperty("time")] 626 | public long Timestamp { get; set; } 627 | public float open { get; set; } 628 | public float high { get; set; } 629 | public float low { get; set; } 630 | public float close { get; set; } 631 | /// 632 | /// Volume in quote currency. 633 | /// 634 | [JsonProperty("volumefrom")] 635 | public float volume { get; set; } 636 | 637 | public Candlestick(long timestamp, float open, float high, float low, float close, float volume) 638 | { 639 | this.Timestamp = timestamp; 640 | this.open = open; 641 | this.high = high; 642 | this.low = low; 643 | //if(close.HasValue) 644 | this.close = close; 645 | //else this.close 646 | this.volume = volume; 647 | } 648 | 649 | public static Candlestick operator *(Candlestick a, Candlestick b) 650 | { 651 | return new Candlestick(a.Timestamp, a.open * b.open, a.high * b.high, a.low * b.low, a.close * b.close, a.volume); 652 | } 653 | } 654 | 655 | public class Trade 656 | { 657 | public long? timestamp { get; set; } 658 | public DateTime? datetime { get; set; } 659 | public string symbol { get; set; } 660 | public string id { get; set; } 661 | public string order { get; set; } 662 | public string type { get; set; } 663 | public string takerOrMaker { get; set; } 664 | public string side { get; set; } 665 | public float? price { get; set; } 666 | public float? cost { get; set; } 667 | public float? amount { get; set; } 668 | public Fee fee { get; set; } 669 | [JsonExtensionData] 670 | public Dictionary info { get; set; } 671 | } 672 | 673 | public class Fee 674 | { 675 | public float? cost { get; set; } 676 | public string currency { get; set; } 677 | public float? rate { get; set; } 678 | } 679 | 680 | public class Has 681 | { 682 | public Capability publicAPI { get; set; } 683 | public Capability privateAPI { get; set; } 684 | public Capability CORS { get; set; } 685 | public Capability cancelOrder { get; set; } 686 | public Capability cancelOrders { get; set; } 687 | public Capability createDepositAddress { get; set; } 688 | public Capability createOrder { get; set; } 689 | public Capability createMarketOrder { get; set; } 690 | public Capability createLimitOrder { get; set; } 691 | public Capability deposit { get; set; } 692 | public Capability editOrder { get; set; } 693 | public Capability fetchBalance { get; set; } 694 | public Capability fetchClosedOrders { get; set; } 695 | public Capability fetchCurrencies { get; set; } 696 | public Capability fetchDepositAddress { get; set; } 697 | public Capability fetchDeposits { get; set; } 698 | public Capability fetchFundingFees { get; set; } 699 | public Capability fetchL2OrderBook { get; set; } 700 | public Capability fetchMarkets { get; set; } 701 | public Capability fetchMyTrades { get; set; } 702 | public Capability fetchOHLCV { get; set; } 703 | public Capability fetchOpenOrders { get; set; } 704 | public Capability fetchOrder { get; set; } 705 | public Capability fetchOrderBook { get; set; } 706 | public Capability fetchOrderBooks { get; set; } 707 | public Capability fetchOrders { get; set; } 708 | public Capability fetchTicker { get; set; } 709 | public Capability fetchTickers { get; set; } 710 | public Capability fetchTrades { get; set; } 711 | public Capability fetchTradingFees { get; set; } 712 | public Capability fetchTradingLimits { get; set; } 713 | public Capability fetchTransactions { get; set; } 714 | public Capability fetchWithdrawals { get; set; } 715 | public Capability withdraw { get; set; } 716 | 717 | public enum Capability { True, False, Emulated } 718 | } 719 | 720 | public class OrderBook 721 | { 722 | public List bids { get; set; } 723 | public List asks { get; set; } 724 | public long? timestamp { get; set; } 725 | public DateTime? datetime { get; set; } 726 | public long? nonce { get; set; } 727 | 728 | public OrderBook(string json) 729 | { 730 | JObject jObject = JObject.Parse(json); 731 | timestamp = jObject["timestamp"].ToObject(); 732 | datetime = jObject["datetime"].ToObject(); 733 | nonce = jObject["nonce"].ToObject(); 734 | asks = new List(); 735 | bids = new List(); 736 | 737 | foreach (var ask in jObject["asks"]) 738 | asks.Add(new BookedOrder(ask[1].ToObject(), ask[0].ToObject())); 739 | 740 | foreach (var bid in jObject["asks"]) 741 | bids.Add(new BookedOrder(bid[1].ToObject(), bid[0].ToObject())); 742 | } 743 | 744 | public class BookedOrder 745 | { 746 | public BookedOrder(float? price, float? amount) 747 | { 748 | this.price = price; 749 | this.amount = amount; 750 | } 751 | 752 | public float? price { get; set; } 753 | public float? amount { get; set; } 754 | } 755 | } 756 | 757 | public class Ticker 758 | { 759 | public string symbol { get; set; } 760 | public long? timestamp { get; set; } 761 | public DateTime? datetime { get; set; } 762 | public float? high { get; set; } 763 | public float? low { get; set; } 764 | public float? bid { get; set; } 765 | public float? bidVolume { get; set; } 766 | public float? ask { get; set; } 767 | public float? askVolume { get; set; } 768 | public float? vwap { get; set; } 769 | public float? open { get; set; } 770 | public float? close { get; set; } 771 | public float? last { get; set; } 772 | public float? previousClose { get; set; } 773 | public float? change { get; set; } 774 | public float? percentage { get; set; } 775 | public float? average { get; set; } 776 | public float? baseVolume { get; set; } 777 | public float? quoteVolume { get; set; } 778 | [JsonExtensionData] 779 | public Dictionary info { get; set; } 780 | } 781 | 782 | public class Currency 783 | { 784 | public int? id { get; set; } 785 | public int? numericId { get; set; } 786 | public string code { get; set; } 787 | public int? precision { get; set; } 788 | } 789 | } 790 | -------------------------------------------------------------------------------- /CCXTSharp/Market.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 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 Market 11 | { 12 | public string id { get; set; } 13 | public string symbol { get; set; } 14 | [JsonProperty("base")] 15 | public string Base { get; set; } 16 | public string quote { get; set; } 17 | public string baseId { get; set; } 18 | public string quoteId { get; set; } 19 | public bool? active { get; set; } 20 | public Precision precision { get; set; } 21 | public Limits limits { get; set; } 22 | 23 | public bool? fee_loaded { get; set; } 24 | public Tiers tiers { get; set; } 25 | public float? taker { get; set; } 26 | public bool? tierBased { get; set; } 27 | public bool? percentage { get; set; } 28 | public float? maker { get; set; } 29 | 30 | [JsonExtensionData] 31 | public Dictionary info { get; set; } 32 | 33 | public class Precision 34 | { 35 | [JsonProperty("base")] 36 | public float? Base { get; set; } 37 | public float? quote { get; set; } 38 | public float? price { get; set; } 39 | public float? amount { get; set; } 40 | } 41 | 42 | public class Tiers 43 | { 44 | public float?[][] taker { get; set; } 45 | public float?[][] maker { get; set; } 46 | } 47 | 48 | public class Limits 49 | { 50 | public Amount amount { get; set; } 51 | public Cost cost { get; set; } 52 | public Price price { get; set; } 53 | } 54 | 55 | public class Amount 56 | { 57 | public float? max { get; set; } 58 | public float? min { get; set; } 59 | } 60 | 61 | public class Cost 62 | { 63 | public float? max { get; set; } 64 | public float? min { get; set; } 65 | } 66 | 67 | public class Price 68 | { 69 | public float? max { get; set; } 70 | public float? min { get; set; } 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /CCXTSharp/NamedPipe.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 | 11 | namespace CCXTSharp 12 | { 13 | class NamedPipe 14 | { 15 | public delegate void OnMessageEventHandler(object sender, OnMessageEventArgs e); 16 | public event OnMessageEventHandler OnMessage; 17 | 18 | private NamedPipeServerStream _serverOut; 19 | private NamedPipeServerStream _serverIn; 20 | private BinaryWriter _binaryWriter; 21 | private BinaryReader _binaryReader; 22 | private readonly object _isWritingLock = new object(); 23 | private bool _serverOutConnected = false; 24 | 25 | public NamedPipe(string pipeNameIn, string pipeNameOut) 26 | { 27 | _serverOut = new NamedPipeServerStream(pipeNameOut); 28 | 29 | _serverIn = new NamedPipeServerStream(pipeNameIn); 30 | _binaryWriter = new BinaryWriter(_serverOut); 31 | _binaryReader = new BinaryReader(_serverIn); 32 | Task.Factory.StartNew(Reader, TaskCreationOptions.LongRunning); 33 | } 34 | 35 | public async Task Write(string str) 36 | { 37 | await Task.Run(() => 38 | { 39 | if (!_serverOutConnected) 40 | { 41 | _serverOut.WaitForConnection(); 42 | _serverOutConnected = true; 43 | } 44 | lock (_isWritingLock) 45 | { 46 | //Console.WriteLine("write started: " + DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds); 47 | byte[] buf = Encoding.ASCII.GetBytes(str); // Get ASCII byte array 48 | _binaryWriter.Write((uint)buf.Length); // Write string length 49 | _binaryWriter.Write(buf); 50 | //Console.WriteLine("write ended: " + DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds); 51 | } 52 | }); 53 | 54 | } 55 | 56 | public async Task Close() 57 | { 58 | await Write("exit"); 59 | _serverIn.Close(); 60 | _serverIn.Dispose(); 61 | _serverOut.Close(); 62 | _serverOut.Dispose(); 63 | } 64 | 65 | private void Reader() 66 | { 67 | _serverIn.WaitForConnection(); 68 | 69 | while (true) 70 | { 71 | try 72 | { 73 | //Console.WriteLine("read started: " + DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds); 74 | var len = (int)_binaryReader.ReadUInt32(); // Read string length 75 | var str = new string(_binaryReader.ReadChars(len)); // Read string 76 | // invoking on another thread so it wouldn't stop reader loop 77 | Task.Run(() => OnMessage?.Invoke(this, new OnMessageEventArgs(str))); 78 | //Console.WriteLine("read ended: " + DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds); 79 | } 80 | catch (EndOfStreamException) 81 | { 82 | break; // When client disconnects 83 | } 84 | 85 | } 86 | } 87 | 88 | public class OnMessageEventArgs : EventArgs 89 | { 90 | public OnMessageEventArgs(string message) 91 | { 92 | Message = message; 93 | } 94 | 95 | public string Message { get; set; } 96 | } 97 | } 98 | } -------------------------------------------------------------------------------- /CCXTSharp/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("CCXTSharp")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("CCXTSharp")] 13 | [assembly: AssemblyCopyright("Copyright © 2018")] 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("52849576-d600-4266-9fe9-f7c87d4c2ac3")] 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 | -------------------------------------------------------------------------------- /CCXTSharp/nuget.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Stock84-dev/CCXTSharp/3fc4312ca67a4e32455e95d8ae4e3fffda63a347/CCXTSharp/nuget.exe -------------------------------------------------------------------------------- /CCXTSharp/nugetPushCommand.txt: -------------------------------------------------------------------------------- 1 | nuget pack CCXTSharp.nuspec 2 | 3 | 4 | nuget push PACKAGE.nupkg -ApiKey YOUR_API_KEY -Source https://api.nuget.org/v3/index.json -Timeout 3600 -------------------------------------------------------------------------------- /CCXTSharp/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /CCXTSharpTest/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /CCXTSharpTest/CCXTSharpTest.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {FAAE9351-367B-4DBF-B09A-88F1E9143365} 8 | Exe 9 | CCXTSharpTest 10 | CCXTSharpTest 11 | v4.7 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 | {52849576-d600-4266-9fe9-f7c87d4c2ac3} 55 | CCXTSharp 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /CCXTSharpTest/Program.cs: -------------------------------------------------------------------------------- 1 | using CCXTSharp; 2 | using System; 3 | using System.Collections; 4 | using System.Collections.Generic; 5 | using System.Diagnostics; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading; 10 | using System.Threading.Tasks; 11 | 12 | namespace CCXTSharpTest 13 | { 14 | class Program 15 | { 16 | static bool loop = true; 17 | static async void f() 18 | { 19 | 20 | Stopwatch sw = new Stopwatch(); 21 | 22 | //CcxtAPI ccxtAPI = new CcxtAPI(@"/home/leon/Documents/projects/CCXTSharp/CCXT/ccxtAPI.py", @"/bin/python"); 23 | CcxtAPI ccxtAPI = new CcxtAPI(@"D:\Documents\New folder\CCXTSharp\CCXT\dist\ccxtAPI.exe"); 24 | //CcxtAPI ccxtAPI = new CcxtAPI(@"D:\Documents\New folder\CCXTSharp\CCXT\ccxtAPI.py", @"C:\Program Files (x86)\Microsoft Visual Studio\Shared\Python36_86\python.exe", true); 25 | long dt = DateTimeOffset.UtcNow.AddHours(-1).ToUnixTimeMilliseconds(); 26 | List c = await ccxtAPI.FetchOHLCV("coinbasepro", "BTC/USD", Timeframe.min1, dt); 27 | foreach (var item in c) 28 | { 29 | Console.WriteLine(item.close); 30 | } 31 | var markets = await ccxtAPI.FetchMarkets("binance"); 32 | markets.ForEach(m => Console.WriteLine(m.symbol)); 33 | await ccxtAPI.Close(); 34 | Console.WriteLine("closed"); 35 | loop = false; 36 | } 37 | 38 | static void Main(string[] args) 39 | { 40 | f(); 41 | while (loop) 42 | { 43 | Task.Delay(1000).Wait(); 44 | } 45 | Task.Delay(10000).Wait(); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /CCXTSharpTest/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("CCXTSharpTest")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("CCXTSharpTest")] 13 | [assembly: AssemblyCopyright("Copyright © 2018")] 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("faae9351-367b-4dbf-b09a-88f1e9143365")] 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 | -------------------------------------------------------------------------------- /Example/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Example/Example.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {4FCF4BB6-A561-441D-A8CA-3B0B897FB313} 8 | Exe 9 | Example 10 | Example 11 | v4.7 12 | 512 13 | true 14 | true 15 | 16 | 17 | 18 | AnyCPU 19 | true 20 | full 21 | false 22 | bin\Debug\ 23 | DEBUG;TRACE 24 | prompt 25 | 4 26 | 27 | 28 | AnyCPU 29 | pdbonly 30 | true 31 | bin\Release\ 32 | TRACE 33 | prompt 34 | 4 35 | 36 | 37 | 38 | ..\packages\CCXTSharp.1.2.0\lib\net45\CCXTSharp.dll 39 | 40 | 41 | ..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /Example/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | using CCXTSharp; 8 | 9 | namespace Example 10 | { 11 | class Program 12 | { 13 | static async void Example1() 14 | { 15 | CcxtAPI ccxtAPI = new CcxtAPI(@"..\..\ccxt\ccxtAPI.exe"); // initialize and start process 16 | List exchangeIds = await ccxtAPI.GetExchangeIds(); // get list of avaliable exchanges 17 | exchangeIds.ForEach(id => Console.WriteLine(id)); // print all exchange ids 18 | await ccxtAPI.Close(); // it's preferred to exit other process if your program will close 19 | } 20 | 21 | static async void Example2() 22 | { 23 | // initialize and run script in python interpreter 24 | CcxtAPI ccxtAPI = new CcxtAPI(@"..\..\ccxt\scripts\ccxtAPI.py", @"D:\Program Files (x86)\Python36-32\python.exe"); 25 | // gets list of markets on Binance exchange and prints their symbols 26 | List markets = await ccxtAPI.FetchMarkets("binance"); 27 | markets.ForEach(m => Console.WriteLine(m.symbol)); 28 | await ccxtAPI.Close(); 29 | } 30 | 31 | static async void Example3() 32 | { 33 | CcxtAPI ccxtAPI = new CcxtAPI(@"..\..\ccxt\ccxtAPI.exe"); 34 | // if cryptopia has support for fetch tickers then fetch them 35 | if((await ccxtAPI.GetExchangeHas("binance")).fetchTickers == Has.Capability.True) 36 | { 37 | Dictionary tickers = await ccxtAPI.FetchTickers("binance"); 38 | // foreach ticker print their symbol and change 39 | foreach (var ticker in tickers.Values) 40 | { 41 | Console.WriteLine(ticker.symbol + ": " + ticker.percentage); 42 | } 43 | } 44 | await ccxtAPI.Close(); 45 | } 46 | 47 | static async void Example4() 48 | { 49 | CcxtAPI ccxtAPI = new CcxtAPI(@"..\..\ccxt\ccxtAPI.exe"); 50 | // Authenticate 51 | await ccxtAPI.ExchangeApiKey("binance", "my_api_key"); 52 | await ccxtAPI.ExchangeSecret("binance", "my_api_secret"); 53 | // Place order 54 | Order order = await ccxtAPI.CreateOrder("binance", "ETH/USDT", OrderType.limit, OrderSide.buy, 1, 210); 55 | // Cancel placed order 56 | await ccxtAPI.CancelOrder("binance", order.id, "ETH/USDT"); 57 | await ccxtAPI.Close(); 58 | } 59 | 60 | static async void Example5() 61 | { 62 | CcxtAPI ccxtAPI = new CcxtAPI(@"..\..\ccxt\ccxtAPI.exe"); 63 | try 64 | { 65 | Dictionary markets = await ccxtAPI.LoadMarkets("_1broker"); 66 | } 67 | catch (CCXTException ex) 68 | { 69 | // exception is handled by ccxt python library 70 | if (ex.HandledByCCXT) 71 | Console.WriteLine(ex.exceptionType.Value.ToString() + ex.Message); 72 | else 73 | { 74 | // there is a bug either in python ccxt or in CCXTSharp 75 | Console.WriteLine(ex.Message); 76 | // restart process 77 | ccxtAPI.Kill(); // message will popup "Failed to execute script ccxtAPI.py" 78 | // reinitialize and continue execution 79 | ccxtAPI = new CcxtAPI(@"..\..\ccxt\ccxtAPI.exe"); 80 | } 81 | } 82 | // ... 83 | await ccxtAPI.Close(); 84 | } 85 | 86 | static async void Example6() 87 | { 88 | CcxtAPI ccxtAPI = new CcxtAPI(@"..\..\ccxt\ccxtAPI.exe"); 89 | List candles = await ccxtAPI.FetchOHLCV("binance", "BTC/USDT", Timeframe.min1); 90 | candles.ForEach(c => Console.WriteLine(c.close)); 91 | await ccxtAPI.Close(); 92 | } 93 | 94 | static async void Test() 95 | { 96 | CcxtAPI ccxtAPI = new CcxtAPI(@"..\..\ccxt\ccxtAPI.exe"); 97 | ccxtAPI.ShowPipeData = true; 98 | Console.WriteLine((await ccxtAPI.GetExchangeHas("yobit")).fetchTickers); 99 | var tickers = await ccxtAPI.FetchTickers("yobit"); 100 | var list = from ticker in tickers.Values 101 | orderby ticker.change 102 | orderby ticker.quoteVolume 103 | select ticker; 104 | 105 | 106 | foreach (var e in list) 107 | { 108 | Console.WriteLine(e.symbol + ": " + e.change + " " + e.quoteVolume); 109 | } 110 | } 111 | 112 | static void Main(string[] args) 113 | { 114 | Example6(); 115 | while (true) 116 | { 117 | Thread.Sleep(100); 118 | } 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /Example/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("Example")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Example")] 13 | [assembly: AssemblyCopyright("Copyright © 2018")] 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("4fcf4bb6-a561-441d-a8ca-3b0b897fb313")] 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 | -------------------------------------------------------------------------------- /Example/ccxt/ccxtAPI.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Stock84-dev/CCXTSharp/3fc4312ca67a4e32455e95d8ae4e3fffda63a347/Example/ccxt/ccxtAPI.exe -------------------------------------------------------------------------------- /Example/ccxt/scripts/CCXTException.py: -------------------------------------------------------------------------------- 1 | class CCXTException: 2 | def __init__(self, exceptionType, message): 3 | self.exceptionType = exceptionType 4 | self.message = message 5 | 6 | -------------------------------------------------------------------------------- /Example/ccxt/scripts/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 | -------------------------------------------------------------------------------- /Example/ccxt/scripts/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 | """ -------------------------------------------------------------------------------- /Example/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Stock84 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CCXTSharp 2 | This is a c# wrapper for popular trading library [ccxt](https://github.com/ccxt/ccxt). Methods and documentation are pretty simmilar to [ccxt](https://github.com/ccxt/ccxt). 3 | 4 | ## Instalation 5 | Package manager 6 | ``` 7 | Install-Package CCXTSharp 8 | ``` 9 | 10 | ## Updating ccxt library 11 | 1. Install python 3.5+ (python 3.7 isn't currently supported by pyinstaller). And add python to system PATH if not already added by instalation. 12 | 2. Open cmd. 13 | 3. pip install ccxt // if libraries are already installed then type pip install ccxt --upgrade 14 | 4. pip install cfscrape 15 | 16 | ## Updating ccxtAPI program 17 | 1. Update ccxt library 18 | 2. Navigate to\your\project\ccxt\scripts in file explorer 19 | 3. shift + right click, Open command window here 20 | 4. pyinstaller -w -F ccxtAPI.py // program will be created in dist folder 21 | 5. replace old file 22 | 23 | ## Architecture 24 | Package contains compiled python code that has original ccxt api. C# part creates new proccess and runs compiled python code. Inter process communication is done with named pipes. 25 | 26 | 27 | ## Examples 28 | List supported exchanges. 29 | ```c# 30 | // initialize and start process 31 | CcxtAPI ccxtAPI = new CcxtAPI(@"..\..\ccxt\ccxtAPI.exe"); 32 | // get list of avaliable exchanges 33 | List exchangeIds = await ccxtAPI.GetExchangIds(); 34 | // print all exchange ids 35 | exchangeIds.ForEach(id => Console.WriteLine(id)); 36 | // it's preferred to exit other process if your program will close 37 | await ccxtAPI.Close(); 38 | ``` 39 | List all markets. 40 | ```c# 41 | // initialize and run script in python interpreter 42 | CcxtAPI ccxtAPI = new CcxtAPI(@"..\..\ccxt\scripts\ccxtAPI.py", @"D:\Program Files (x86)\Python36-32\python.exe"); 43 | // gets list of markets on Binance exchange and prints their symbols 44 | List markets = await ccxtAPI.FetchMarkets("binance"); 45 | markets.ForEach(m => Console.WriteLine(m.symbol)); 46 | await ccxtAPI.Close(); 47 | ``` 48 | Checking for implementation. 49 | ```c# 50 | CcxtAPI ccxtAPI = new CcxtAPI(@"..\..\ccxt\ccxtAPI.exe"); 51 | // if cryptopia has support for fetch tickers then fetch them 52 | if((await ccxtAPI.GetExchangeHas("cryptopia")).fetchTickers == Has.Capability.True) 53 | { 54 | Dictionary tickers = await ccxtAPI.FetchTickers("cryptopia"); 55 | // foreach ticker print their symbol and change 56 | foreach (var ticker in tickers.Values) 57 | { 58 | Console.WriteLine(ticker.symbol + ": " + ticker.change); 59 | } 60 | } 61 | await ccxtAPI.Close(); 62 | ``` 63 | Private API. 64 | ```c# 65 | CcxtAPI ccxtAPI = new CcxtAPI(@"..\..\ccxt\ccxtAPI.exe"); 66 | // Authenticate 67 | await ccxtAPI.ExchangeApiKey("binance", "my_api_key"); 68 | await ccxtAPI.ExchangeSecret("binance", "my_api_secret"); 69 | // Place order 70 | Order order = await ccxtAPI.CreateOrder("binance", "ETH/USDT", OrderType.limit, OrderSide.buy, 1, 210); 71 | // Cancel placed order 72 | await ccxtAPI.CancelOrder("binance", order.id, "ETH/USDT"); 73 | await ccxtAPI.Close(); 74 | ``` 75 | Exception handling 76 | ```c# 77 | CcxtAPI ccxtAPI = new CcxtAPI(@"..\..\ccxt\ccxtAPI.exe"); 78 | try 79 | { 80 | Dictionary markets = await ccxtAPI.LoadMarkets("_1broker"); 81 | } 82 | catch (CCXTException ex) 83 | { 84 | // exception is handled by ccxt python library 85 | if (ex.HandledByCCXT) 86 | Console.WriteLine(ex.exceptionType.Value.ToString() + ex.Message); 87 | else 88 | { 89 | // there is a bug either in python ccxt or in CCXTSharp 90 | Console.WriteLine(ex.Message); 91 | // restart process 92 | ccxtAPI.Kill(); // message will popup "Failed to execute script ccxtAPI.py" 93 | // reinitialize and continue execution 94 | ccxtAPI = new CcxtAPI(@"..\..\ccxt\ccxtAPI.exe"); 95 | } 96 | } 97 | // ... 98 | await ccxtAPI.Close(); 99 | ``` 100 | 101 | 102 | 103 | --------------------------------------------------------------------------------