├── web ├── src │ ├── assets │ │ ├── .gitkeep │ │ ├── page-logo.png │ │ ├── background.jpg │ │ └── early-dumb-donate.png │ ├── app │ │ ├── shared │ │ │ ├── classes │ │ │ │ ├── config.ts │ │ │ │ └── wallet.ts │ │ │ ├── orders │ │ │ │ ├── orders.component.scss │ │ │ │ ├── orders.component.html │ │ │ │ ├── orders.component.ts │ │ │ │ └── orders.component.spec.ts │ │ │ ├── buy-sell │ │ │ │ ├── buy-sell.component.scss │ │ │ │ ├── buy-sell.component.html │ │ │ │ ├── buy-sell.component.ts │ │ │ │ └── buy-sell.component.spec.ts │ │ │ ├── my-orders │ │ │ │ ├── my-orders.component.scss │ │ │ │ ├── my-orders.component.html │ │ │ │ ├── my-orders.component.ts │ │ │ │ └── my-orders.component.spec.ts │ │ │ ├── buy-sell-form │ │ │ │ ├── buy-sell-form.component.scss │ │ │ │ ├── buy-sell-form.component.html │ │ │ │ ├── buy-sell-form.component.ts │ │ │ │ └── buy-sell-form.component.spec.ts │ │ │ ├── price-history │ │ │ │ ├── price-history.component.scss │ │ │ │ ├── price-history.component.html │ │ │ │ ├── price-history.component.ts │ │ │ │ └── price-history.component.spec.ts │ │ │ ├── trade-history │ │ │ │ ├── trade-history.component.scss │ │ │ │ ├── trade-history.component.html │ │ │ │ ├── trade-history.component.ts │ │ │ │ └── trade-history.component.spec.ts │ │ │ ├── selected-currency │ │ │ │ ├── selected-currency.component.scss │ │ │ │ ├── selected-currency.component.ts │ │ │ │ ├── selected-currency.component.html │ │ │ │ └── selected-currency.component.spec.ts │ │ │ ├── all-enabled-currency-tickers │ │ │ │ ├── all-enabled-currency-tickers.component.scss │ │ │ │ ├── all-enabled-currency-tickers.component.html │ │ │ │ ├── all-enabled-currency-tickers.component.spec.ts │ │ │ │ └── all-enabled-currency-tickers.component.ts │ │ │ ├── navbar │ │ │ │ ├── navbar.component.scss │ │ │ │ ├── navbar.component.ts │ │ │ │ ├── navbar.component.html │ │ │ │ └── navbar.component.spec.ts │ │ │ ├── exchange-currency-ticker │ │ │ │ ├── exchange-currency-ticker.component.scss │ │ │ │ ├── exchange-currency-ticker.component.spec.ts │ │ │ │ ├── exchange-currency-ticker.component.ts │ │ │ │ └── exchange-currency-ticker.component.html │ │ │ └── theme-picker │ │ │ │ ├── theme-picker.html │ │ │ │ ├── theme-picker.spec.ts │ │ │ │ ├── theme-picker.scss │ │ │ │ └── theme-picker.ts │ │ ├── pages │ │ │ ├── about │ │ │ │ ├── about.component.scss │ │ │ │ ├── about.component.html │ │ │ │ ├── about.component.ts │ │ │ │ └── about.component.spec.ts │ │ │ ├── history │ │ │ │ ├── history.component.scss │ │ │ │ ├── history.component.html │ │ │ │ ├── history.component.ts │ │ │ │ └── history.component.spec.ts │ │ │ ├── exchange-grid │ │ │ │ ├── exchange-grid.component.scss │ │ │ │ ├── exchange-grid.component.ts │ │ │ │ ├── exchange-grid.component.spec.ts │ │ │ │ └── exchange-grid.component.html │ │ │ ├── home │ │ │ │ ├── home.component.scss │ │ │ │ ├── home.component.html │ │ │ │ ├── home.component.ts │ │ │ │ └── home.component.spec.ts │ │ │ ├── currency-list │ │ │ │ ├── currency-list.component.scss │ │ │ │ ├── currency-list.component.ts │ │ │ │ ├── currency-list.component.spec.ts │ │ │ │ └── currency-list.component.html │ │ │ ├── trading │ │ │ │ ├── trading.component.scss │ │ │ │ ├── trading.component.html │ │ │ │ ├── trading.component.ts │ │ │ │ └── trading.component.spec.ts │ │ │ ├── settings │ │ │ │ ├── settings.component.scss │ │ │ │ └── settings.component.spec.ts │ │ │ ├── wallet │ │ │ │ ├── wallet.component.scss │ │ │ │ ├── wallet.component.spec.ts │ │ │ │ └── wallet.component.ts │ │ │ ├── donate │ │ │ │ ├── donate.component.scss │ │ │ │ ├── donate.component.ts │ │ │ │ ├── donate.component.html │ │ │ │ └── donate.component.spec.ts │ │ │ └── dashboard │ │ │ │ ├── dashboard.component.scss │ │ │ │ ├── dashboard.component.spec.ts │ │ │ │ ├── dashboard.component.html │ │ │ │ └── dashboard.component.ts │ │ ├── services │ │ │ ├── sidebar │ │ │ │ ├── sidebar.service.spec.ts │ │ │ │ └── sidebar.service.ts │ │ │ ├── websocket │ │ │ │ ├── websocket.service.spec.ts │ │ │ │ └── websocket.service.ts │ │ │ ├── websocket-handler │ │ │ │ ├── websocket-handler.service.spec.ts │ │ │ │ └── websocket-handler.service.ts │ │ │ ├── theme-storage │ │ │ │ ├── theme-storage.service.ts │ │ │ │ └── theme-storage.service.spec.ts │ │ │ └── style-manager │ │ │ │ ├── style-manager.service.ts │ │ │ │ └── style-manager.service.spec.ts │ │ ├── app.component.spec.ts │ │ ├── providers │ │ │ └── electron.service.ts │ │ ├── app.component.scss │ │ ├── app.component.ts │ │ ├── app-routing.module.ts │ │ └── app.component.html │ ├── favicon.ico │ ├── typings.d.ts │ ├── environments │ │ ├── index.prod.ts │ │ └── index.ts │ ├── tsconfig.app.json │ ├── main.ts │ ├── tsconfig.spec.json │ ├── index.html │ ├── test.ts │ ├── styles.scss │ └── polyfills.ts ├── _config.yml ├── logo-angular.jpg ├── logo-electron.jpg ├── .travis.yml ├── e2e │ ├── app.po.ts │ ├── tsconfig.e2e.json │ └── app.e2e-spec.ts ├── .editorconfig ├── tsconfig.json ├── .gitignore ├── protractor.conf.js ├── .angular-cli.json ├── karma.conf.js ├── package.js ├── main.ts ├── README.md └── tslint.json ├── Dockerfile ├── tools ├── portfolio │ └── portfolio_test.go └── config │ ├── config_test.go │ └── config.go ├── .gitignore ├── CONTRIBUTORS ├── docker-compose.yml ├── .travis.yml ├── testdata └── test.sh ├── main_test.go ├── currency ├── symbol │ ├── symbol_test.go │ └── symbol.go └── translation │ ├── translation.go │ └── translation_test.go ├── exchanges ├── huobi │ ├── huobi_types.go │ ├── huobi_test.go │ └── huobi_wrapper.go ├── coinut │ ├── coinut_test.go │ └── coinut_websocket.go ├── okcoin │ └── okcoin_test.go ├── bitfinex │ ├── bitfinex_wrapper_test.go │ └── bitfinex_websocket_test.go ├── alphapoint │ ├── alphapoint_websocket.go │ └── alphapoint_wrapper.go ├── localbitcoins │ ├── localbitcoins_test.go │ └── localbitcoins_wrapper.go ├── nonce │ ├── nonce.go │ └── nonce_test.go ├── poloniex │ └── poloniex_test.go ├── bitstamp │ └── bitstamp_websocket.go ├── btcc │ ├── btcc_test.go │ ├── btcc_websocket.go │ └── btcc_wrapper.go ├── anx │ ├── anx_types.go │ └── anx_wrapper.go ├── kraken │ └── kraken_types.go ├── lakebtc │ ├── lakebtc_test.go │ └── lakebtc_types.go ├── liqui │ ├── liqui_test.go │ └── liqui_types.go ├── gdax │ └── gdax_websocket.go ├── wex │ └── wex_test.go ├── itbit │ ├── itbit_wrapper.go │ └── itbit_test.go └── btcmarkets │ └── btcmarkets_test.go ├── doc └── coding_style.md ├── orders_test.go ├── LICENSE ├── orders.go ├── config ├── config_encryption_test.go └── config_encryption.go └── restful_router.go /web/src/assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/src/app/shared/classes/config.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-architect -------------------------------------------------------------------------------- /web/src/app/pages/about/about.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/src/app/pages/history/history.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/src/app/shared/orders/orders.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/src/app/shared/buy-sell/buy-sell.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/src/app/shared/my-orders/my-orders.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/src/app/pages/exchange-grid/exchange-grid.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/src/app/shared/buy-sell-form/buy-sell-form.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/src/app/shared/price-history/price-history.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/src/app/shared/trade-history/trade-history.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/src/app/shared/selected-currency/selected-currency.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:onbuild 2 | COPY config_example.dat config.dat 3 | 4 | -------------------------------------------------------------------------------- /web/src/app/pages/about/about.component.html: -------------------------------------------------------------------------------- 1 |

2 | about works! 3 |

4 | -------------------------------------------------------------------------------- /web/src/app/pages/history/history.component.html: -------------------------------------------------------------------------------- 1 |

2 | history works! 3 |

4 | -------------------------------------------------------------------------------- /web/src/app/pages/home/home.component.scss: -------------------------------------------------------------------------------- 1 | .example-card { 2 | width: 400px; 3 | } -------------------------------------------------------------------------------- /web/src/app/shared/all-enabled-currency-tickers/all-enabled-currency-tickers.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/logo-angular.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dimillian/gocryptotrader/master/web/logo-angular.jpg -------------------------------------------------------------------------------- /web/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dimillian/gocryptotrader/master/web/src/favicon.ico -------------------------------------------------------------------------------- /web/logo-electron.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dimillian/gocryptotrader/master/web/logo-electron.jpg -------------------------------------------------------------------------------- /web/src/app/shared/buy-sell-form/buy-sell-form.component.html: -------------------------------------------------------------------------------- 1 |

2 | buy-sell-form works! 3 |

4 | -------------------------------------------------------------------------------- /web/src/assets/page-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dimillian/gocryptotrader/master/web/src/assets/page-logo.png -------------------------------------------------------------------------------- /web/src/assets/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dimillian/gocryptotrader/master/web/src/assets/background.jpg -------------------------------------------------------------------------------- /tools/portfolio/portfolio_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "testing" 4 | 5 | func TestMain(t *testing.T) { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /web/src/assets/early-dumb-donate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dimillian/gocryptotrader/master/web/src/assets/early-dumb-donate.png -------------------------------------------------------------------------------- /web/.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "7" 4 | - "6" 5 | install: 6 | - npm install 7 | script: 8 | - npm run build 9 | -------------------------------------------------------------------------------- /web/src/app/shared/navbar/navbar.component.scss: -------------------------------------------------------------------------------- 1 | .material-icons { 2 | cursor: pointer; 3 | } 4 | 5 | .flex-spacer { 6 | flex-grow: 1; 7 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | config.json 2 | config.dat 3 | node_modules 4 | lib 5 | .vscode 6 | 7 | testdata/dump 8 | testdata/writefiletest 9 | 10 | # InteliJ 11 | .idea 12 | *.iml 13 | -------------------------------------------------------------------------------- /web/src/app/pages/currency-list/currency-list.component.scss: -------------------------------------------------------------------------------- 1 | .BTC { 2 | color:orange; 3 | } 4 | .LTC { 5 | color:silver; 6 | } 7 | .ETH { 8 | color:darkslategrey; 9 | } -------------------------------------------------------------------------------- /web/src/app/pages/trading/trading.component.scss: -------------------------------------------------------------------------------- 1 | 2 | .mat-fab { 3 | top: auto; 4 | right: 30px; 5 | bottom: 20px; 6 | left: auto; 7 | position: fixed; 8 | z-index: 3; 9 | } -------------------------------------------------------------------------------- /web/src/app/shared/orders/orders.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Orders 4 | 5 | -------------------------------------------------------------------------------- /web/src/app/pages/trading/trading.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /web/src/app/shared/my-orders/my-orders.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | My Orders 4 | 5 | -------------------------------------------------------------------------------- /web/e2e/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, element, by } from 'protractor'; 2 | 3 | /* tslint:disable */ 4 | export class AngularElectronPage { 5 | navigateTo(route: string) { 6 | return browser.get(route); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /web/src/app/shared/price-history/price-history.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Price History 4 | 5 | -------------------------------------------------------------------------------- /web/src/app/shared/trade-history/trade-history.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Trade History 4 | 5 | -------------------------------------------------------------------------------- /web/src/app/pages/settings/settings.component.scss: -------------------------------------------------------------------------------- 1 | // FAB 2 | .mat-fab { 3 | top: auto; 4 | right: 30px; 5 | bottom: 20px; 6 | left: auto; 7 | position: fixed; 8 | } 9 | 10 | .form-content { 11 | margin: 20px; 12 | } -------------------------------------------------------------------------------- /web/src/app/pages/wallet/wallet.component.scss: -------------------------------------------------------------------------------- 1 | .wallet-card { 2 | width: 80%; 3 | margin: 10px auto; 4 | } 5 | 6 | .BTC { 7 | color:orange; 8 | } 9 | .LTC { 10 | color:silver; 11 | } 12 | .ETH { 13 | color:darkslategrey; 14 | } -------------------------------------------------------------------------------- /web/src/app/pages/donate/donate.component.scss: -------------------------------------------------------------------------------- 1 | .BTC { 2 | color:orange; 3 | } 4 | 5 | .full-card { 6 | width: 30%; 7 | margin: 0px auto !important; 8 | } 9 | 10 | .heart { 11 | margin: 0px auto !important; 12 | display:flex; 13 | } -------------------------------------------------------------------------------- /web/src/typings.d.ts: -------------------------------------------------------------------------------- 1 | /* SystemJS module definition */ 2 | declare var nodeModule: NodeModule; 3 | interface NodeModule { 4 | id: string; 5 | } 6 | 7 | declare var window: Window; 8 | interface Window { 9 | process: any; 10 | require: any; 11 | } 12 | -------------------------------------------------------------------------------- /web/src/environments/index.prod.ts: -------------------------------------------------------------------------------- 1 | // This file contains production variables. (When you work in PROD MODE) 2 | // This file is use by webpack. Please don't rename it and don't move it to another directory. 3 | export const environment = { 4 | production: true 5 | }; 6 | -------------------------------------------------------------------------------- /web/src/environments/index.ts: -------------------------------------------------------------------------------- 1 | // This file contains development variables. (When you work in DEV MODE) 2 | // This file is use by webpack. Please don't rename it and don't move it to another directory. 3 | export const environment = { 4 | production: false 5 | }; 6 | -------------------------------------------------------------------------------- /web/e2e/tsconfig.e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/e2e", 5 | "module": "commonjs", 6 | "target": "es5", 7 | "types":[ 8 | "jasmine", 9 | "node" 10 | ] 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /web/.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /CONTRIBUTORS: -------------------------------------------------------------------------------- 1 | Thanks to the following contributors: 2 | 3 | Scott - gloriousCode 4 | Ryan O'Hara-Reid - shazbert 5 | Jacob Gadikian - faddat 6 | Cornel - cornelk 7 | Łukasz Kurowski - crackcomm 8 | Adrian Gallagher - thrasher- 9 | Manuel Kreutz - 140am 10 | libsora.so - if1live 11 | Tong - tongxiaofeng 12 | Jamie Cheng - starit 13 | Jake - snipesjr -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | services: 3 | web: 4 | build: web/ 5 | hostname: gocryptotraderweb 6 | container_name: web 7 | ports: 8 | - "3333:80" 9 | cli: 10 | build: . 11 | hostname: gocryptotrader 12 | container_name: daemon 13 | privileged: true 14 | -------------------------------------------------------------------------------- /web/src/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "module": "es2015", 6 | "baseUrl": "", 7 | "types": [] 8 | }, 9 | "exclude": [ 10 | "test.ts", 11 | "**/*.spec.ts", 12 | "dist", 13 | "app-builds", 14 | "node_modules" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /web/src/app/pages/about/about.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-about', 5 | templateUrl: './about.component.html', 6 | styleUrls: ['./about.component.scss'] 7 | }) 8 | export class AboutComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /web/src/app/pages/donate/donate.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-donate', 5 | templateUrl: './donate.component.html', 6 | styleUrls: ['./donate.component.scss'] 7 | }) 8 | export class DonateComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /web/src/app/shared/orders/orders.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-orders', 5 | templateUrl: './orders.component.html', 6 | styleUrls: ['./orders.component.scss'] 7 | }) 8 | export class OrdersComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /web/src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from 'environments'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(AppModule); 12 | -------------------------------------------------------------------------------- /web/src/app/pages/history/history.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-history', 5 | templateUrl: './history.component.html', 6 | styleUrls: ['./history.component.scss'] 7 | }) 8 | export class HistoryComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /web/src/app/pages/home/home.component.html: -------------------------------------------------------------------------------- 1 | 2 |

Welcome to GoCryptoTrader

3 |

GoCryptoTrader is a multi-currency, multi-exchange trader for cryptocurrencies

4 |

It is under active development and you can see its development progress by clicking the trello button to the left

5 |

If you like what you see, consider clicking the donation button to the left

-------------------------------------------------------------------------------- /web/src/app/pages/trading/trading.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-trading', 5 | templateUrl: './trading.component.html', 6 | styleUrls: ['./trading.component.scss'] 7 | }) 8 | export class TradingComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /web/src/app/shared/my-orders/my-orders.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-my-orders', 5 | templateUrl: './my-orders.component.html', 6 | styleUrls: ['./my-orders.component.scss'] 7 | }) 8 | export class MyOrdersComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /web/src/app/shared/buy-sell/buy-sell.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | BUY 5 | 6 | 7 | 8 | 9 | 10 | SELL 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /web/src/app/pages/home/home.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-home', 5 | templateUrl: './home.component.html', 6 | styleUrls: ['./home.component.scss'], 7 | 8 | }) 9 | export class HomeComponent implements OnInit { 10 | title = `App works !`; 11 | 12 | constructor() { } 13 | 14 | ngOnInit() { 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /web/src/app/pages/currency-list/currency-list.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-currency-list', 5 | templateUrl: './currency-list.component.html', 6 | styleUrls: ['./currency-list.component.scss'] 7 | }) 8 | export class CurrencyListComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /web/src/app/pages/exchange-grid/exchange-grid.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-exchange-grid', 5 | templateUrl: './exchange-grid.component.html', 6 | styleUrls: ['./exchange-grid.component.scss'] 7 | }) 8 | export class ExchangeGridComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /web/src/app/shared/buy-sell-form/buy-sell-form.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-buy-sell-form', 5 | templateUrl: './buy-sell-form.component.html', 6 | styleUrls: ['./buy-sell-form.component.scss'] 7 | }) 8 | export class BuySellFormComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /web/src/app/shared/price-history/price-history.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-price-history', 5 | templateUrl: './price-history.component.html', 6 | styleUrls: ['./price-history.component.scss'] 7 | }) 8 | export class PriceHistoryComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /web/src/app/shared/trade-history/trade-history.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-trade-history', 5 | templateUrl: './trade-history.component.html', 6 | styleUrls: ['./trade-history.component.scss'] 7 | }) 8 | export class TradeHistoryComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /web/src/app/shared/selected-currency/selected-currency.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-selected-currency', 5 | templateUrl: './selected-currency.component.html', 6 | styleUrls: ['./selected-currency.component.scss'] 7 | }) 8 | export class SelectedCurrencyComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /web/src/app/shared/buy-sell/buy-sell.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit,Directive, ViewContainerRef } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-buy-sell', 5 | templateUrl: './buy-sell.component.html', 6 | styleUrls: ['./buy-sell.component.scss'] 7 | }) 8 | export class BuySellComponent implements OnInit { 9 | 10 | constructor(public viewContainerRef: ViewContainerRef) { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /web/e2e/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AngularElectronPage } from './app.po'; 2 | import { browser, element, by } from 'protractor'; 3 | 4 | describe('angular-electron App', () => { 5 | let page: AngularElectronPage; 6 | 7 | beforeEach(() => { 8 | page = new AngularElectronPage(); 9 | }); 10 | 11 | it('should display message saying App works !', () => { 12 | expect(element(by.css('app-home h1')).getText()).toMatch('App works !'); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.8.x 5 | #- master 6 | 7 | before_install: 8 | - go get -t -v ./... 9 | 10 | script: 11 | - ./testdata/test.sh 12 | 13 | install: 14 | - go get github.com/gorilla/websocket 15 | - go get github.com/toorop/go-pusher 16 | - go get github.com/thrasher-/socketio 17 | - go get github.com/beatgammit/turnpike 18 | - go get github.com/gorilla/mux 19 | 20 | after_success: 21 | - bash <(curl -s https://codecov.io/bash) 22 | -------------------------------------------------------------------------------- /tools/config/config_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "testing" 4 | 5 | func TestEncryptOrDecrypt(t *testing.T) { 6 | reValue := EncryptOrDecrypt(true) 7 | if reValue != "encrypted" { 8 | t.Error( 9 | "Test failed - Tools/Config/Config_test.go - EncryptOrDecrypt Error", 10 | ) 11 | } 12 | reValue = EncryptOrDecrypt(false) 13 | if reValue != "decrypted" { 14 | t.Error( 15 | "Test failed - Tools/Config/Config_test.go - EncryptOrDecrypt Error", 16 | ) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /web/src/app/pages/dashboard/dashboard.component.scss: -------------------------------------------------------------------------------- 1 | .full-card { 2 | margin: 10px; 3 | width: 100%; 4 | } 5 | 6 | ::ng-deep mat-grid-tile.mat-grid-tile .mat-figure { 7 | align-items: initial; 8 | /*vertical alignment*/ 9 | } 10 | 11 | .mat-card-footer { 12 | position: absolute; 13 | bottom: 24px; 14 | } 15 | 16 | .mat-fab { 17 | top: auto; 18 | right: 30px; 19 | bottom: 20px; 20 | left: auto; 21 | position: fixed; 22 | z-index: 3; 23 | } 24 | -------------------------------------------------------------------------------- /web/src/app/services/sidebar/sidebar.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | 3 | import { SidebarService } from './sidebar.service'; 4 | 5 | describe('SidebarService', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [SidebarService] 9 | }); 10 | }); 11 | 12 | it('should be created', inject([SidebarService], (service: SidebarService) => { 13 | expect(service).toBeTruthy(); 14 | })); 15 | }); 16 | -------------------------------------------------------------------------------- /web/src/app/shared/exchange-currency-ticker/exchange-currency-ticker.component.scss: -------------------------------------------------------------------------------- 1 | .one-time-animation { 2 | animation: one-time-animation 2s forwards 1; 3 | } 4 | @keyframes one-time-animation { 5 | from { 6 | background: green; 7 | } 8 | to { 9 | background: transparent; 10 | } 11 | } 12 | 13 | /* do the following changes */ 14 | 15 | .selected { 16 | box-shadow: 0 0 10px green inset; 17 | transition: box-shadow 2s ease; 18 | } 19 | -------------------------------------------------------------------------------- /web/src/app/shared/all-enabled-currency-tickers/all-enabled-currency-tickers.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /web/src/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/spec", 5 | "module": "commonjs", 6 | "target": "es5", 7 | "baseUrl": "", 8 | "types": [ 9 | "jasmine", 10 | "node" 11 | ] 12 | }, 13 | "files": [ 14 | "test.ts" 15 | ], 16 | "include": [ 17 | "**/*.spec.ts", 18 | "**/*.d.ts" 19 | ], 20 | "exclude": [ 21 | "dist", 22 | "app-builds", 23 | "node_modules" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /web/src/app/services/websocket/websocket.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | 3 | import { WebsocketService } from './websocket.service'; 4 | 5 | describe('WebsocketService', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [WebsocketService] 9 | }); 10 | }); 11 | 12 | it('should be created', inject([WebsocketService], (service: WebsocketService) => { 13 | expect(service).toBeTruthy(); 14 | })); 15 | }); 16 | -------------------------------------------------------------------------------- /web/src/app/shared/selected-currency/selected-currency.component.html: -------------------------------------------------------------------------------- 1 | 2 | 7 | -------------------------------------------------------------------------------- /testdata/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | if [ -n "$TRAVIS_BUILD_DIR" ]; then 6 | cd $TRAVIS_BUILD_DIR 7 | else 8 | cd $GOPATH/src/github.com/thrasher-/gocryptotrader 9 | fi 10 | 11 | echo "" > testdata/coverage.txt 12 | 13 | for d in $(go list ./... | grep -v vendor); do 14 | go test -race -coverprofile=profile.out -covermode=atomic -cover $d 15 | if [ -f profile.out ]; then 16 | cat profile.out >> testdata/coverage.txt 17 | rm profile.out 18 | fi 19 | done 20 | -------------------------------------------------------------------------------- /web/src/app/shared/classes/wallet.ts: -------------------------------------------------------------------------------- 1 | 2 | export interface CoinTotal { 3 | coin: string; 4 | balance: number; 5 | percentage: number; 6 | address: string; 7 | icon:string; 8 | } 9 | 10 | export interface Summary { 11 | BTC: CoinTotal[]; 12 | ETH: CoinTotal[]; 13 | LTC: CoinTotal[]; 14 | } 15 | 16 | export interface Wallet { 17 | coin_totals: CoinTotal[]; 18 | coins_offline: CoinTotal[]; 19 | offline_summary: Summary; 20 | coins_online: CoinTotal[]; 21 | online_summary: Summary; 22 | } -------------------------------------------------------------------------------- /web/src/app/shared/navbar/navbar.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { SidebarService } from './../../services/sidebar/sidebar.service'; 3 | 4 | @Component({ 5 | selector: 'app-navbar', 6 | templateUrl: './navbar.component.html', 7 | styleUrls: ['./navbar.component.scss'] 8 | }) 9 | export class NavbarComponent implements OnInit { 10 | sidebarService: SidebarService 11 | constructor(something: SidebarService) { 12 | this.sidebarService = something; 13 | } 14 | 15 | ngOnInit() { 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /web/src/app/services/websocket-handler/websocket-handler.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | 3 | import { WebsocketHandlerService } from './websocket-handler.service'; 4 | 5 | describe('WebsocketHandlerService', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [WebsocketHandlerService] 9 | }); 10 | }); 11 | 12 | it('should be created', inject([WebsocketHandlerService], (service: WebsocketHandlerService) => { 13 | expect(service).toBeTruthy(); 14 | })); 15 | }); 16 | -------------------------------------------------------------------------------- /main_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "testing" 4 | 5 | func TestSetupBotExchanges(t *testing.T) { 6 | // setupBotExchanges() 7 | } 8 | 9 | func TestMain(t *testing.T) { 10 | // Nothing 11 | } 12 | 13 | func TestAdjustGoMaxProcs(t *testing.T) { 14 | AdjustGoMaxProcs() 15 | } 16 | 17 | func TestHandleInterrupt(t *testing.T) { 18 | HandleInterrupt() 19 | } 20 | 21 | func TestShutdown(t *testing.T) { 22 | // Nothing 23 | } 24 | 25 | func TestSeedExchangeAccountInfo(t *testing.T) { 26 | SeedExchangeAccountInfo(GetAllEnabledExchangeAccountInfo().Data) 27 | } 28 | -------------------------------------------------------------------------------- /currency/symbol/symbol_test.go: -------------------------------------------------------------------------------- 1 | package symbol 2 | 3 | import "testing" 4 | 5 | func TestGetSymbolByCurrencyName(t *testing.T) { 6 | expected := "₩" 7 | actual, err := GetSymbolByCurrencyName("KPW") 8 | if err != nil { 9 | t.Errorf("Test failed. TestGetSymbolByCurrencyName error: %s", err) 10 | } 11 | 12 | if actual != expected { 13 | t.Errorf("Test failed. TestGetSymbolByCurrencyName differing values") 14 | } 15 | 16 | _, err = GetSymbolByCurrencyName("BLAH") 17 | if err == nil { 18 | t.Errorf("Test failed. TestGetSymbolByCurrencyNam returned nil on non-existent currency") 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /exchanges/huobi/huobi_types.go: -------------------------------------------------------------------------------- 1 | package huobi 2 | 3 | // Ticker holds ticker information 4 | type Ticker struct { 5 | High float64 6 | Low float64 7 | Last float64 8 | Vol float64 9 | Buy float64 10 | Sell float64 11 | } 12 | 13 | // TickerResponse holds the initial response type 14 | type TickerResponse struct { 15 | Time string 16 | Ticker Ticker 17 | } 18 | 19 | // Orderbook holds the order book information 20 | type Orderbook struct { 21 | ID float64 22 | TS float64 23 | Bids [][]float64 `json:"bids"` 24 | Asks [][]float64 `json:"asks"` 25 | Symbol string `json:"string"` 26 | } 27 | -------------------------------------------------------------------------------- /web/src/app/shared/navbar/navbar.component.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /web/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "module":"system", 5 | "outDir": "./dist/out-tsc", 6 | "baseUrl": "src", 7 | "sourceMap": true, 8 | "declaration": false, 9 | "moduleResolution": "node", 10 | "emitDecoratorMetadata": true, 11 | "experimentalDecorators": true, 12 | "allowJs": false, 13 | "target": "es5", 14 | "paths": { 15 | "environments": [ 16 | "./environments" 17 | ] 18 | }, 19 | "types": [ 20 | "node", 21 | "jasmine" 22 | ], 23 | "typeRoots": [ 24 | "node_modules/@types" 25 | ], 26 | "lib": [ 27 | "es2016", 28 | "dom" 29 | ] 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /web/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | GoCryptoTrader 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /web/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | /app-builds 8 | 9 | # dependencies 10 | /node_modules 11 | 12 | # IDEs and editors 13 | /.idea 14 | .project 15 | .classpath 16 | .c9/ 17 | *.launch 18 | .settings/ 19 | *.sublime-workspace 20 | 21 | # IDE - VSCode 22 | .vscode/* 23 | !.vscode/settings.json 24 | !.vscode/tasks.json 25 | !.vscode/launch.json 26 | !.vscode/extensions.json 27 | 28 | # misc 29 | /.sass-cache 30 | /connect.lock 31 | /coverage 32 | /libpeerconnection.log 33 | npm-debug.log 34 | testem.log 35 | /typings 36 | 37 | # e2e 38 | /e2e/*.js 39 | /e2e/*.map 40 | 41 | # System Files 42 | .DS_Store 43 | Thumbs.db 44 | -------------------------------------------------------------------------------- /exchanges/coinut/coinut_test.go: -------------------------------------------------------------------------------- 1 | package coinut 2 | 3 | // 4 | // const ( 5 | // apiKey = "" 6 | // apiSecret = "" 7 | // ) 8 | // 9 | // var c COINUT 10 | // 11 | // func TestSetDefaults(t *testing.T) { 12 | // c.SetDefaults() 13 | // } 14 | // 15 | // func TestSetup(t *testing.T) { 16 | // exch := config.ExchangeConfig{} 17 | // c.Setup(exch) 18 | // 19 | // exch.Enabled = true 20 | // exch.APIKey = apiKey 21 | // exch.APISecret = apiSecret 22 | // c.Setup(exch) 23 | // } 24 | // 25 | // // func TestGetInstruments(t *testing.T) { 26 | // // c.Verbose = true 27 | // // resp, err := c.GetInstruments() 28 | // // if err == nil { 29 | // // t.Error("Test failed - GetInstruments() error", err) 30 | // // } 31 | // // log.Println(resp) 32 | // // } 33 | -------------------------------------------------------------------------------- /web/src/app/pages/about/about.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { AboutComponent } from './about.component'; 4 | 5 | describe('AboutComponent', () => { 6 | let component: AboutComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ AboutComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(AboutComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should be created', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /web/src/app/pages/donate/donate.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Donations 4 | We give our thanks 5 | 6 | 7 | 8 |

If this framework helped you in any way, or you would like to support the developers working on it, please donate

9 | 10 | 11 | attach_money 12 |

Address:

13 |

1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB

14 |
15 |
16 |
17 |
-------------------------------------------------------------------------------- /web/src/app/pages/donate/donate.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { DonateComponent } from './donate.component'; 4 | 5 | describe('DonateComponent', () => { 6 | let component: DonateComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ DonateComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(DonateComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /web/src/app/pages/wallet/wallet.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { WalletComponent } from './wallet.component'; 4 | 5 | describe('WalletComponent', () => { 6 | let component: WalletComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ WalletComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(WalletComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should be created', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /web/src/app/shared/navbar/navbar.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { NavbarComponent } from './navbar.component'; 4 | 5 | describe('NavbarComponent', () => { 6 | let component: NavbarComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ NavbarComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(NavbarComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should be created', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /web/src/app/shared/orders/orders.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { OrdersComponent } from './orders.component'; 4 | 5 | describe('OrdersComponent', () => { 6 | let component: OrdersComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ OrdersComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(OrdersComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /web/src/app/pages/history/history.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { HistoryComponent } from './history.component'; 4 | 5 | describe('HistoryComponent', () => { 6 | let component: HistoryComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ HistoryComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(HistoryComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /web/src/app/pages/trading/trading.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { TradingComponent } from './trading.component'; 4 | 5 | describe('TradingComponent', () => { 6 | let component: TradingComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ TradingComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(TradingComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /web/src/app/shared/buy-sell/buy-sell.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { BuySellComponent } from './buy-sell.component'; 4 | 5 | describe('BuySellComponent', () => { 6 | let component: BuySellComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ BuySellComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(BuySellComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /web/src/app/pages/settings/settings.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { SettingsComponent } from './settings.component'; 4 | 5 | describe('SettingsComponent', () => { 6 | let component: SettingsComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ SettingsComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(SettingsComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should be created', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /web/src/app/shared/my-orders/my-orders.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { MyOrdersComponent } from './my-orders.component'; 4 | 5 | describe('MyOrdersComponent', () => { 6 | let component: MyOrdersComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ MyOrdersComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(MyOrdersComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /web/src/app/pages/dashboard/dashboard.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { DashboardComponent } from './dashboard.component'; 4 | 5 | describe('DashboardComponent', () => { 6 | let component: DashboardComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ DashboardComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(DashboardComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should be created', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /web/src/app/shared/theme-picker/theme-picker.html: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | 10 |
11 |
12 | check_circle 13 |
14 |
15 |
16 |
17 |
18 |
-------------------------------------------------------------------------------- /web/src/app/shared/theme-picker/theme-picker.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ThemePickerComponent } from './theme-picker.component'; 4 | 5 | describe('ThemePickerComponent', () => { 6 | let component: ThemePickerComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ ThemePickerComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ThemePickerComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /web/src/app/shared/buy-sell-form/buy-sell-form.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { BuySellFormComponent } from './buy-sell-form.component'; 4 | 5 | describe('BuySellFormComponent', () => { 6 | let component: BuySellFormComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ BuySellFormComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(BuySellFormComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /exchanges/okcoin/okcoin_test.go: -------------------------------------------------------------------------------- 1 | package okcoin 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/thrasher-/gocryptotrader/config" 7 | ) 8 | 9 | var o OKCoin 10 | 11 | // Please supply your own APIKEYS here for due diligence testing 12 | 13 | const ( 14 | apiKey = "" 15 | apiSecret = "" 16 | ) 17 | 18 | func TestSetDefaults(t *testing.T) { 19 | o.SetDefaults() 20 | } 21 | 22 | func TestSetup(t *testing.T) { 23 | cfg := config.GetConfig() 24 | cfg.LoadConfig("../../testdata/configtest.json") 25 | okcoinConfig, err := cfg.GetExchangeConfig("OKCOIN International") 26 | if err != nil { 27 | t.Error("Test Failed - OKCoin Setup() init error") 28 | } 29 | 30 | okcoinConfig.AuthenticatedAPISupport = true 31 | okcoinConfig.APIKey = apiKey 32 | okcoinConfig.APISecret = apiSecret 33 | 34 | o.Setup(okcoinConfig) 35 | } 36 | -------------------------------------------------------------------------------- /web/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, async } from '@angular/core/testing'; 2 | import { RouterTestingModule } from '@angular/router/testing'; 3 | import { AppComponent } from './app.component'; 4 | import { ElectronService } from 'app/providers/electron.service'; 5 | 6 | describe('AppComponent', () => { 7 | beforeEach(async(() => { 8 | TestBed.configureTestingModule({ 9 | declarations: [ 10 | AppComponent 11 | ], 12 | providers : [ 13 | ElectronService 14 | ], 15 | imports: [RouterTestingModule] 16 | }).compileComponents(); 17 | })); 18 | 19 | it('should create the app', async(() => { 20 | const fixture = TestBed.createComponent(AppComponent); 21 | const app = fixture.debugElement.componentInstance; 22 | expect(app).toBeTruthy(); 23 | })); 24 | }); 25 | -------------------------------------------------------------------------------- /web/src/app/pages/currency-list/currency-list.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { CurrencyListComponent } from './currency-list.component'; 4 | 5 | describe('CurrencyListComponent', () => { 6 | let component: CurrencyListComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ CurrencyListComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(CurrencyListComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /web/src/app/pages/exchange-grid/exchange-grid.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ExchangeGridComponent } from './exchange-grid.component'; 4 | 5 | describe('ExchangeGridComponent', () => { 6 | let component: ExchangeGridComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ ExchangeGridComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ExchangeGridComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /web/src/app/shared/price-history/price-history.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { PriceHistoryComponent } from './price-history.component'; 4 | 5 | describe('PriceHistoryComponent', () => { 6 | let component: PriceHistoryComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ PriceHistoryComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(PriceHistoryComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /web/src/app/shared/trade-history/trade-history.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { TradeHistoryComponent } from './trade-history.component'; 4 | 5 | describe('TradeHistoryComponent', () => { 6 | let component: TradeHistoryComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ TradeHistoryComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(TradeHistoryComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /web/src/app/shared/selected-currency/selected-currency.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { SelectedCurrencyComponent } from './selected-currency.component'; 4 | 5 | describe('SelectedCurrencyComponent', () => { 6 | let component: SelectedCurrencyComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ SelectedCurrencyComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(SelectedCurrencyComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /currency/translation/translation.go: -------------------------------------------------------------------------------- 1 | package translation 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/thrasher-/gocryptotrader/currency/pair" 7 | ) 8 | 9 | var translations = map[pair.CurrencyItem]pair.CurrencyItem{ 10 | "BTC": "XBT", 11 | "DOGE": "XDG", 12 | "USD": "USDT", 13 | } 14 | 15 | // GetTranslation returns similar strings for a particular currency 16 | func GetTranslation(currency pair.CurrencyItem) (pair.CurrencyItem, error) { 17 | result, ok := translations[currency] 18 | if !ok { 19 | return "", errors.New("no translation found for specified currency") 20 | } 21 | 22 | return result, nil 23 | } 24 | 25 | // HasTranslation returns whether or not a particular currency has a translation 26 | func HasTranslation(currency pair.CurrencyItem) bool { 27 | _, ok := translations[currency] 28 | if !ok { 29 | return false 30 | } 31 | return true 32 | } 33 | -------------------------------------------------------------------------------- /web/src/app/providers/electron.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | // If you import a module but never use any of the imported values other than as TypeScript types, 4 | // the resulting javascript file will look as if you never imported the module at all. 5 | import { ipcRenderer } from 'electron'; 6 | import * as childProcess from 'child_process'; 7 | 8 | @Injectable() 9 | export class ElectronService { 10 | 11 | ipcRenderer: typeof ipcRenderer; 12 | childProcess: typeof childProcess; 13 | 14 | constructor() { 15 | // Conditional imports 16 | if (this.isElectron()) { 17 | this.ipcRenderer = window.require('electron').ipcRenderer; 18 | this.childProcess = window.require('child_process'); 19 | } 20 | } 21 | 22 | isElectron = () => { 23 | return window && window.process && window.process.type; 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /web/src/app/shared/exchange-currency-ticker/exchange-currency-ticker.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ExchangeCurrencyTickerComponent } from './exchange-currency-ticker.component'; 4 | 5 | describe('ExchangeCurrencyTickerComponent', () => { 6 | let component: ExchangeCurrencyTickerComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ ExchangeCurrencyTickerComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ExchangeCurrencyTickerComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should be created', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /web/src/app/shared/exchange-currency-ticker/exchange-currency-ticker.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Input } from '@angular/core'; 2 | @Component({ 3 | selector: 'app-exchange-currency-ticker', 4 | templateUrl: './exchange-currency-ticker.component.html', 5 | styleUrls: ['./exchange-currency-ticker.component.scss'], 6 | }) 7 | export class ExchangeCurrencyTickerComponent implements OnInit { 8 | @Input('ticker') ticker: TickerUpdate; 9 | 10 | constructor() {} 11 | ngOnInit() { } 12 | 13 | } 14 | 15 | 16 | export interface CurrencyPair { 17 | delimiter: string; 18 | first_currency: string; 19 | second_currency: string; 20 | } 21 | 22 | export interface TickerUpdate { 23 | Pair: CurrencyPair; 24 | CurrencyPair: string; 25 | Last: number; 26 | High: number; 27 | Low: number; 28 | Bid: number; 29 | Ask: number; 30 | Volume: number; 31 | PriceATH: number; 32 | Exchange:string; 33 | } 34 | 35 | -------------------------------------------------------------------------------- /web/src/app/shared/all-enabled-currency-tickers/all-enabled-currency-tickers.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { AllEnabledCurrencyTickersComponent } from './all-enabled-currency-tickers.component'; 4 | 5 | describe('AllEnabledCurrencyTickersComponent', () => { 6 | let component: AllEnabledCurrencyTickersComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ AllEnabledCurrencyTickersComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(AllEnabledCurrencyTickersComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should be created', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /exchanges/bitfinex/bitfinex_wrapper_test.go: -------------------------------------------------------------------------------- 1 | package bitfinex 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/thrasher-/gocryptotrader/currency/pair" 7 | "github.com/thrasher-/gocryptotrader/exchanges/ticker" 8 | ) 9 | 10 | func TestStart(t *testing.T) { 11 | start := Bitfinex{} 12 | start.Start() 13 | } 14 | 15 | func TestRun(t *testing.T) { 16 | run := Bitfinex{} 17 | run.Run() 18 | } 19 | 20 | func TestGetTickerPrice(t *testing.T) { 21 | getTickerPrice := Bitfinex{} 22 | _, err := getTickerPrice.GetTickerPrice(pair.NewCurrencyPair("BTC", "USD"), 23 | ticker.Spot) 24 | if err != nil { 25 | t.Errorf("Test Failed - Bitfinex GetTickerPrice() error: %s", err) 26 | } 27 | } 28 | 29 | func TestGetOrderbookEx(t *testing.T) { 30 | getOrderBookEx := Bitfinex{} 31 | _, err := getOrderBookEx.GetOrderbookEx(pair.NewCurrencyPair("BTC", "USD"), 32 | ticker.Spot) 33 | if err != nil { 34 | t.Errorf("Test Failed - Bitfinex GetOrderbookEx() error: %s", err) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /web/src/app/app.component.scss: -------------------------------------------------------------------------------- 1 | .container { 2 | height: 100vh; 3 | min-height:100%; 4 | } 5 | 6 | .main { 7 | display: flex; 8 | align-items: center; 9 | justify-content: center; 10 | padding: 20px 70px 0; 11 | min-width: 86%; 12 | margin: 10px auto; 13 | overflow-x: hidden; 14 | margin-top:4rem; 15 | } 16 | 17 | .sidebar { 18 | width: 14%; 19 | position: fixed; 20 | margin-top: 4rem; 21 | } 22 | 23 | .navbar { 24 | position: fixed; 25 | width: 100%; 26 | z-index: 2; 27 | } 28 | 29 | .dashboard-highlight { 30 | color: green !important; 31 | } 32 | 33 | .trading-highlight { 34 | color: orangered !important; 35 | } 36 | 37 | .history-highlight { 38 | color: cornflowerblue !important; 39 | } 40 | 41 | .wallet-highlight { 42 | color: blueviolet !important; 43 | } 44 | 45 | .settings-highlight { 46 | color: magenta !important; 47 | } 48 | 49 | .donate-highlight { 50 | color: goldenrod!important; 51 | } -------------------------------------------------------------------------------- /doc/coding_style.md: -------------------------------------------------------------------------------- 1 | Coding Style 2 | =============== 3 | 4 | In order to maintain a consistent style across the codebase, the following coding style has been adopted: 5 | 6 | - Function names use PascalCase (func SomeFunc()). 7 | - Function names using acronyms are capitilised (func SendHTTPRequest()). 8 | - Variable names use CamelCase (var someVar()). 9 | - Coding style uses gofmt. 10 | - Const variables are CamelCase depending on exported items. 11 | - In line with gofmt, for loops and if statements don't require paranthesis. 12 | 13 | Block style example: 14 | ```go 15 | func SendHTTPRequest(method, path string, headers map[string]string, body io.Reader) (string, error) { 16 | result := strings.ToUpper(method) 17 | 18 | if result != "POST" && result != "GET" && result != "DELETE" { 19 | return "", errors.New("Invalid HTTP method specified.") 20 | } 21 | 22 | req, err := http.NewRequest(method, path, body) 23 | 24 | if err != nil { 25 | return "", err 26 | } 27 | 28 | for k, v := range headers { 29 | req.Header.Add(k, v) 30 | } 31 | ... 32 | } 33 | ``` 34 | -------------------------------------------------------------------------------- /orders_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestNewOrder(t *testing.T) { 8 | ID := NewOrder("ANX", 2000, 20.00) 9 | if ID != 0 { 10 | t.Error("Test Failed - Orders_test.go NewOrder() - Error") 11 | } 12 | ID = NewOrder("BATMAN", 400, 25.00) 13 | if ID != 1 { 14 | t.Error("Test Failed - Orders_test.go NewOrder() - Error") 15 | } 16 | } 17 | 18 | func TestDeleteOrder(t *testing.T) { 19 | if value := DeleteOrder(0); !value { 20 | t.Error("Test Failed - Orders_test.go DeleteOrder() - Error") 21 | } 22 | if value := DeleteOrder(100); value { 23 | t.Error("Test Failed - Orders_test.go DeleteOrder() - Error") 24 | } 25 | } 26 | 27 | func TestGetOrdersByExchange(t *testing.T) { 28 | if value := GetOrdersByExchange("ANX"); len(value) != 0 { 29 | t.Error("Test Failed - Orders_test.go GetOrdersByExchange() - Error") 30 | } 31 | } 32 | 33 | func TestGetOrderByOrderID(t *testing.T) { 34 | if value := GetOrderByOrderID(69); value != nil { 35 | t.Error("Test Failed - Orders_test.go GetOrdersByExchange() - Error") 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /web/src/app/pages/dashboard/dashboard.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 | 7 | 8 | 9 | {{tile.title}} 10 | {{tile.subTitle}} 11 | 12 | 13 | {{tile.content}} 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | -------------------------------------------------------------------------------- /web/src/app/services/theme-storage/theme-storage.service.ts: -------------------------------------------------------------------------------- 1 | import {Injectable, EventEmitter} from '@angular/core'; 2 | 3 | export interface DocsSiteTheme { 4 | href: string; 5 | accent: string; 6 | primary: string; 7 | isDark?: boolean; 8 | isDefault?: boolean; 9 | } 10 | 11 | 12 | @Injectable() 13 | export class ThemeStorageService { 14 | static storageKey = 'docs-theme-storage-current'; 15 | 16 | public onThemeUpdate: EventEmitter = new EventEmitter(); 17 | 18 | public storeTheme(theme: DocsSiteTheme) { 19 | try { 20 | window.localStorage[ThemeStorageService.storageKey] = JSON.stringify(theme); 21 | } catch (e) { } 22 | 23 | this.onThemeUpdate.emit(theme); 24 | } 25 | 26 | public getStoredTheme(): DocsSiteTheme { 27 | try { 28 | return JSON.parse(window.localStorage[ThemeStorageService.storageKey] || null); 29 | } catch (e) { 30 | return null; 31 | } 32 | } 33 | 34 | public clearStorage() { 35 | try { 36 | window.localStorage.removeItem(ThemeStorageService.storageKey); 37 | } catch (e) { } 38 | } 39 | } -------------------------------------------------------------------------------- /web/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // Protractor configuration file, see link for more information 2 | // https://github.com/angular/protractor/blob/master/lib/config.ts 3 | 4 | const { SpecReporter } = require('jasmine-spec-reporter'); 5 | 6 | exports.config = { 7 | allScriptsTimeout: 25000, 8 | getPageTimeout: 15000, 9 | delayBrowserTimeInSeconds: 0, 10 | specs: [ 11 | './e2e/**/*.e2e-spec.ts' 12 | ], 13 | capabilities: { 14 | 'browserName': 'chrome', 15 | chromeOptions: { 16 | binary: './node_modules/electron/dist/electron.exe', 17 | args: ['--test-type=webdriver', 'app=dist/main.js'] 18 | } 19 | }, 20 | directConnect: true, 21 | baseUrl: 'http://localhost:4200/', 22 | framework: 'jasmine2', 23 | jasmineNodeOpts: { 24 | showColors: true, 25 | defaultTimeoutInterval: 30000, 26 | print: function () { } 27 | }, 28 | beforeLaunch: function () { 29 | require('ts-node').register({ 30 | project: 'e2e/tsconfig.e2e.json' 31 | }); 32 | }, 33 | onPrepare() { 34 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 35 | } 36 | }; 37 | -------------------------------------------------------------------------------- /exchanges/huobi/huobi_test.go: -------------------------------------------------------------------------------- 1 | package huobi 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/thrasher-/gocryptotrader/config" 7 | ) 8 | 9 | var h HUOBI 10 | 11 | // Please supply your own APIKEYS here for due diligence testing 12 | 13 | const ( 14 | apiKey = "" 15 | apiSecret = "" 16 | ) 17 | 18 | func TestSetDefaults(t *testing.T) { 19 | h.SetDefaults() 20 | } 21 | 22 | func TestSetup(t *testing.T) { 23 | cfg := config.GetConfig() 24 | cfg.LoadConfig("../../testdata/configtest.json") 25 | huobiConfig, err := cfg.GetExchangeConfig("Huobi") 26 | if err != nil { 27 | t.Error("Test Failed - Huobi Setup() init error") 28 | } 29 | 30 | huobiConfig.AuthenticatedAPISupport = true 31 | huobiConfig.APIKey = apiKey 32 | huobiConfig.APISecret = apiSecret 33 | 34 | h.Setup(huobiConfig) 35 | } 36 | 37 | func TestGetFee(t *testing.T) { 38 | t.Parallel() 39 | if h.GetFee() != 0 { 40 | t.Error("test failed - Huobi GetFee() error") 41 | } 42 | } 43 | 44 | func TestGetTicker(t *testing.T) { 45 | t.Parallel() 46 | _, err := h.GetTicker("btcusd") 47 | if err == nil { 48 | t.Error("test failed - Huobi GetTicker() error", err) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014-2017 The GoCryptoTrader Developers 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. 22 | 23 | -------------------------------------------------------------------------------- /web/src/app/shared/exchange-currency-ticker/exchange-currency-ticker.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{ticker?.Exchange}} {{ticker?.CurrencyPair}} Ticker update 4 | 5 | 6 | 7 | 8 |

Last: {{ticker?.Last | number:'1.0-1'}}

9 |
10 | 11 |

Low: {{ticker?.Low | number:'1.0-1'}}

12 |
13 | 14 |

High: {{ticker?.High | number:'1.0-1'}}

15 |
16 | 17 | 18 |

Bid: {{ticker?.Bid | number:'1.0-1'}}

19 |
20 | 21 |

Ask: {{ticker?.Ask | number:'1.0-1'}}

22 |
23 | 24 |

Volume: {{ticker?.Volume | number:'1.0-1'}}

25 |
26 |
27 |
28 | 29 | -------------------------------------------------------------------------------- /web/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/long-stack-trace-zone'; 4 | import 'zone.js/dist/proxy.js'; 5 | import 'zone.js/dist/sync-test'; 6 | import 'zone.js/dist/jasmine-patch'; 7 | import 'zone.js/dist/async-test'; 8 | import 'zone.js/dist/fake-async-test'; 9 | import { getTestBed } from '@angular/core/testing'; 10 | import { 11 | BrowserDynamicTestingModule, 12 | platformBrowserDynamicTesting 13 | } from '@angular/platform-browser-dynamic/testing'; 14 | 15 | // Unfortunately there's no typing for the `__karma__` variable. Just declare it as any. 16 | declare const __karma__: any; 17 | declare const require: any; 18 | 19 | // Prevent Karma from running prematurely. 20 | __karma__.loaded = function () {}; 21 | 22 | // First, initialize the Angular testing environment. 23 | getTestBed().initTestEnvironment( 24 | BrowserDynamicTestingModule, 25 | platformBrowserDynamicTesting() 26 | ); 27 | // Then we find all the tests. 28 | const context = require.context('./', true, /\.spec\.ts$/); 29 | // And load the modules. 30 | context.keys().map(context); 31 | // Finally, start Karma to run the tests. 32 | __karma__.start(); 33 | -------------------------------------------------------------------------------- /web/src/app/pages/home/home.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { HomeComponent } from './home.component'; 4 | 5 | describe('HomeComponent', () => { 6 | let component: HomeComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ HomeComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(HomeComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | 26 | it(`should have as title 'App works !'`, async(() => { 27 | fixture = TestBed.createComponent(HomeComponent); 28 | const app = fixture.debugElement.componentInstance; 29 | expect(app.title).toEqual('App works !'); 30 | })); 31 | 32 | it('should render title in a h1 tag', async(() => { 33 | fixture = TestBed.createComponent(HomeComponent); 34 | fixture.detectChanges(); 35 | const compiled = fixture.debugElement.nativeElement; 36 | expect(compiled.querySelector('h1').textContent).toContain('App works !'); 37 | })); 38 | }); 39 | -------------------------------------------------------------------------------- /web/src/app/services/websocket/websocket.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import {Subject, Observable, Observer } from 'rxjs/Rx'; 3 | 4 | @Injectable() 5 | export class WebsocketService { 6 | constructor() { } 7 | 8 | private subject: Subject; 9 | 10 | public connect(url): Subject { 11 | if (!this.subject) { 12 | this.subject = this.create(url); 13 | } 14 | return this.subject; 15 | } 16 | 17 | private authenticateMessage = { 18 | Event:'auth', 19 | data:{"username":"admin","password":"e7cf3ef4f17c3999a94f2c6f612e8a888e5b1026878e4e19398b23bd38ec221a"}, 20 | } 21 | 22 | private isAuth = false; 23 | 24 | private create(url): Subject { 25 | let ws = new WebSocket(url); 26 | 27 | let observable = Observable.create( 28 | (obs: Observer) => { 29 | ws.onmessage = obs.next.bind(obs); 30 | ws.onerror = obs.error.bind(obs); 31 | ws.onclose = obs.complete.bind(obs); 32 | return ws.close.bind(ws); 33 | }) 34 | let observer = { 35 | next: (data: any) => { 36 | if (ws.readyState === WebSocket.OPEN) { 37 | ws.send(JSON.stringify(this.authenticateMessage)); 38 | 39 | ws.send(JSON.stringify(data)); 40 | } 41 | } 42 | } 43 | return Subject.create(observer, observable); 44 | } 45 | } -------------------------------------------------------------------------------- /web/src/app/pages/currency-list/currency-list.component.html: -------------------------------------------------------------------------------- 1 | 2 |

Poloniex

3 | 4 | attach_money 5 |

BTC_USD

6 | 7 |
8 | 9 | attach_money 10 |

LTC_USD

11 | 12 |
13 | 14 | attach_money 15 |

ETH_USD

16 | 17 |
18 | 19 |

Kraken

20 | 21 | attach_money 22 |

BTC_USD

23 | 24 |
25 | 26 | attach_money 27 |

LTC_USD

28 | 29 |
30 | 31 | attach_money 32 |

ETH_USD

33 | 34 |
35 |
-------------------------------------------------------------------------------- /currency/translation/translation_test.go: -------------------------------------------------------------------------------- 1 | package translation 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/thrasher-/gocryptotrader/currency/pair" 7 | ) 8 | 9 | func TestGetTranslation(t *testing.T) { 10 | currencyPair := pair.NewCurrencyPair("BTC", "USD") 11 | expected := pair.CurrencyItem("XBT") 12 | actual, err := GetTranslation(currencyPair.FirstCurrency) 13 | if err != nil { 14 | t.Error("GetTranslation: failed to retrieve translation for BTC") 15 | } 16 | 17 | if expected != actual { 18 | t.Error("GetTranslation: translation result was different to expected result") 19 | } 20 | 21 | currencyPair.FirstCurrency = "ETH" 22 | _, err = GetTranslation(currencyPair.FirstCurrency) 23 | if err == nil { 24 | t.Error("GetTranslation: no error on non translatable currency") 25 | } 26 | } 27 | 28 | func TestHasTranslation(t *testing.T) { 29 | currencyPair := pair.NewCurrencyPair("BTC", "USD") 30 | expected := true 31 | actual := HasTranslation(currencyPair.FirstCurrency) 32 | if expected != actual { 33 | t.Error("HasTranslation: translation result was different to expected result") 34 | } 35 | 36 | currencyPair.FirstCurrency = "ETH" 37 | expected = false 38 | actual = HasTranslation(currencyPair.FirstCurrency) 39 | if expected != actual { 40 | t.Error("HasTranslation: translation result was different to expected result") 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /web/.angular-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "project": { 4 | "name": "angular-electron" 5 | }, 6 | "apps": [ 7 | { 8 | "root": "src", 9 | "outDir": "dist", 10 | "assets": [ 11 | "assets", 12 | "favicon.ico" 13 | ], 14 | "index": "index.html", 15 | "main": "main.ts", 16 | "polyfills": "polyfills.ts", 17 | "test": "test.ts", 18 | "tsconfig": "tsconfig.app.json", 19 | "testTsconfig": "tsconfig.spec.json", 20 | "prefix": "app", 21 | "styles": [ 22 | "styles.scss" 23 | ], 24 | "scripts": [ 25 | ], 26 | "environmentSource": "environments/environment.ts", 27 | "environments": { 28 | "dev": "environments/environment.ts", 29 | "prod": "environments/environment.prod.ts" 30 | } 31 | } 32 | ], 33 | "e2e": { 34 | "protractor": { 35 | "config": "./protractor.conf.js" 36 | } 37 | }, 38 | "lint": [ 39 | { 40 | "project": "src/tsconfig.app.json" 41 | }, 42 | { 43 | "project": "src/tsconfig.spec.json" 44 | }, 45 | { 46 | "project": "e2e/tsconfig.e2e.json" 47 | } 48 | ], 49 | "test": { 50 | "karma": { 51 | "config": "./karma.conf.js" 52 | } 53 | }, 54 | "defaults": { 55 | "styleExt": "scss", 56 | "component": { 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /web/src/app/services/websocket-handler/websocket-handler.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Observable, Subject } from 'rxjs/Rx'; 3 | import { WebsocketService } from './../../services/websocket/websocket.service'; 4 | 5 | const WEBSOCKET_URL = 'ws://localhost:9050/ws'; 6 | 7 | export interface Message { 8 | Event: string, 9 | data:any, 10 | Exchange:string, 11 | AssetType:string 12 | } 13 | 14 | @Injectable() 15 | export class WebsocketHandlerService { 16 | public messages: Subject; 17 | 18 | private authenticateMessage = { 19 | Event:'auth', 20 | data:{"username":"admin","password":"e7cf3ef4f17c3999a94f2c6f612e8a888e5b1026878e4e19398b23bd38ec221a"}, 21 | } 22 | 23 | public authenticate() { 24 | this.messages.next(this.authenticateMessage); 25 | } 26 | 27 | constructor(wsService: WebsocketService) { 28 | this.messages = >wsService 29 | .connect(WEBSOCKET_URL) 30 | .map((response: MessageEvent): Message => { 31 | 32 | let data = JSON.parse(response.data); 33 | // variables aren't consistent yet. Here's a hack! 34 | var dataData = data.Data === undefined ? data.data : data.Data; 35 | var eventEvent = data.Event === undefined ? data.event : data.Event; 36 | return { 37 | Event: eventEvent, 38 | data: dataData, 39 | Exchange: data.exchange, 40 | AssetType: data.assetType 41 | } 42 | }); 43 | } 44 | } -------------------------------------------------------------------------------- /web/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/0.13/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular/cli'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular/cli/plugins/karma') 14 | ], 15 | client:{ 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | files: [ 19 | { pattern: './src/test.ts', watched: false } 20 | ], 21 | preprocessors: { 22 | './src/test.ts': ['@angular/cli'] 23 | }, 24 | mime: { 25 | 'text/x-typescript': ['ts','tsx'] 26 | }, 27 | coverageIstanbulReporter: { 28 | reports: [ 'html', 'lcovonly' ], 29 | fixWebpackSourcePaths: true 30 | }, 31 | angularCli: { 32 | environment: 'dev' 33 | }, 34 | reporters: config.angularCli && config.angularCli.codeCoverage 35 | ? ['progress', 'coverage-istanbul'] 36 | : ['progress', 'kjhtml'], 37 | port: 9876, 38 | colors: true, 39 | logLevel: config.LOG_INFO, 40 | autoWatch: true, 41 | browsers: ['Chrome'], 42 | singleRun: false 43 | }); 44 | }; 45 | -------------------------------------------------------------------------------- /web/src/app/services/style-manager/style-manager.service.ts: -------------------------------------------------------------------------------- 1 | import {Injectable} from '@angular/core'; 2 | /** 3 | * Class for managing stylesheets. Stylesheets are loaded into named slots so that they can be 4 | * removed or changed later. 5 | */ 6 | @Injectable() 7 | export class StyleManagerService { 8 | /** 9 | * Set the stylesheet with the specified key. 10 | */ 11 | setStyle(key: string, href: string) { 12 | getLinkElementForKey(key).setAttribute('href', href); 13 | } 14 | 15 | /** 16 | * Remove the stylesheet with the specified key. 17 | */ 18 | removeStyle(key: string) { 19 | const existingLinkElement = getExistingLinkElementByKey(key); 20 | if (existingLinkElement) { 21 | document.head.removeChild(existingLinkElement); 22 | } 23 | } 24 | } 25 | 26 | function getLinkElementForKey(key: string) { 27 | return getExistingLinkElementByKey(key) || createLinkElementWithKey(key); 28 | } 29 | 30 | function getExistingLinkElementByKey(key: string) { 31 | return document.head.querySelector(`link[rel="stylesheet"].${getClassNameForKey(key)}`); 32 | } 33 | 34 | function createLinkElementWithKey(key: string) { 35 | const linkEl = document.createElement('link'); 36 | linkEl.setAttribute('rel', 'stylesheet'); 37 | linkEl.classList.add(getClassNameForKey(key)); 38 | document.head.appendChild(linkEl); 39 | return linkEl; 40 | } 41 | 42 | function getClassNameForKey(key: string) { 43 | return `style-manager-${key}`; 44 | } -------------------------------------------------------------------------------- /web/src/styles.scss: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | 3 | html, 4 | body { 5 | margin: 0; 6 | padding: 0; 7 | font-family: Roboto,Helvetica Neue Light,Helvetica Neue,Helvetica,Arial,Lucida Grande,sans-serif; 8 | } 9 | 10 | .loading-spinner { 11 | margin-left: 50%; 12 | margin-right: 50%; 13 | } 14 | 15 | //////////////////////////////////////////////////////////////// 16 | // Default settings for cards 17 | //////////////////////////////////////////////////////////////// 18 | .card { 19 | width: 80%; 20 | margin: 10px auto; 21 | } 22 | 23 | //////////////////////////////////////////////////////////////// 24 | // Default settings for expandable tile menus 25 | //////////////////////////////////////////////////////////////// 26 | .mat-expansion-panel { 27 | width: 80%; 28 | margin: 0px auto !important; 29 | } 30 | 31 | .mat-expansion-panel-header-title, 32 | .mat-expansion-panel-header-description { 33 | flex-basis: 0; 34 | } 35 | 36 | .mat-expansion-panel-header-description { 37 | justify-content: space-between; 38 | align-items: center; 39 | } 40 | 41 | .mat-expansion-panel-spacing { 42 | margin: 16px auto !important; 43 | width: 85%; 44 | } 45 | 46 | .mat-drawer { 47 | background: none !important; 48 | } 49 | 50 | @import '~@angular/material/prebuilt-themes/indigo-pink.css'; 51 | 52 | /* 53 | deeppurple-amber.css 54 | indigo-pink.css 55 | pink-bluegrey.css 56 | purple-green.css 57 | */ -------------------------------------------------------------------------------- /web/src/app/services/sidebar/sidebar.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { MatSidenav, MatDrawerToggleResult } from '@angular/material'; 3 | 4 | @Injectable() 5 | export class SidebarService { 6 | private sidenav: MatSidenav; 7 | 8 | /** 9 | * Setter for sidenav. 10 | * 11 | * @param {MatSidnav} sidenav 12 | */ 13 | public setSidenav(sidenav: MatSidenav) { 14 | this.sidenav = sidenav; 15 | } 16 | 17 | /** 18 | * Open this sidenav, and return a Promise that will resolve when it's fully opened (or get rejected if it didn't). 19 | * 20 | * @returns Promise 21 | */ 22 | public open(): Promise { 23 | this.sidenav.open(); 24 | 25 | return; 26 | } 27 | 28 | /** 29 | * Close this sidenav, and return a Promise that will resolve when it's fully closed (or get rejected if it didn't). 30 | * 31 | * @returns Promise 32 | */ 33 | public close(): Promise { 34 | this.sidenav.close(); 35 | return; 36 | } 37 | 38 | /** 39 | * Toggle this sidenav. This is equivalent to calling open() when it's already opened, or close() when it's closed. 40 | * 41 | * @param {boolean} isOpen Whether the sidenav should be open. 42 | * 43 | * @returns {Promise} 44 | */ 45 | public toggle(isOpen?: boolean): Promise { 46 | this.sidenav.toggle(isOpen); 47 | return; 48 | } 49 | } -------------------------------------------------------------------------------- /web/src/app/shared/theme-picker/theme-picker.scss: -------------------------------------------------------------------------------- 1 | $theme-picker-menu-padding: 8px; 2 | $theme-picker-grid-cell-size: 48px; 3 | $theme-picker-grid-cells-per-row: 2; 4 | $theme-picker-swatch-size: 36px; 5 | $theme-picker-accent-stripe-size: 6px; 6 | 7 | 8 | .docs-theme-picker-menu { 9 | .mat-menu-content { 10 | padding: $theme-picker-menu-padding; 11 | } 12 | 13 | [mat-menu-item] { 14 | flex: 0 0 auto; 15 | padding: 0; 16 | overflow: hidden; 17 | } 18 | 19 | .docs-theme-picker-swatch { 20 | position: relative; 21 | width: $theme-picker-swatch-size; 22 | height: $theme-picker-swatch-size; 23 | margin: ($theme-picker-grid-cell-size - $theme-picker-swatch-size) / 2; 24 | border-radius: 50%; 25 | overflow: hidden; 26 | 27 | .docs-theme-chosen-icon { 28 | color: white; 29 | position: absolute; 30 | left: 50%; top: 50%; 31 | transform: translate(-50%, -50%); 32 | } 33 | 34 | &::after { 35 | content: ''; 36 | position: absolute; 37 | top: 0; 38 | left: 0; 39 | width: 100%; 40 | height: 100%; 41 | box-sizing: border-box; 42 | border: 1px solid rgba(0,0,0,.2); 43 | border-radius: 50%; 44 | } 45 | } 46 | 47 | .docs-theme-picker-primary { 48 | width: 100%; 49 | height: 100%; 50 | } 51 | 52 | .docs-theme-picker-accent { 53 | position: absolute; 54 | bottom: $theme-picker-accent-stripe-size; 55 | width: 100%; 56 | height: $theme-picker-accent-stripe-size; 57 | } 58 | } -------------------------------------------------------------------------------- /exchanges/coinut/coinut_websocket.go: -------------------------------------------------------------------------------- 1 | package coinut 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | 7 | "github.com/gorilla/websocket" 8 | "github.com/thrasher-/gocryptotrader/common" 9 | ) 10 | 11 | const COINUT_WEBSOCKET_URL = "wss://wsapi.coinut.com" 12 | 13 | func (c *COINUT) WebsocketClient() { 14 | for c.Enabled && c.Websocket { 15 | var Dialer websocket.Dialer 16 | var err error 17 | c.WebsocketConn, _, err = Dialer.Dial(c.WebsocketURL, http.Header{}) 18 | 19 | if err != nil { 20 | log.Printf("%s Unable to connect to Websocket. Error: %s\n", c.Name, err) 21 | continue 22 | } 23 | 24 | if c.Verbose { 25 | log.Printf("%s Connected to Websocket.\n", c.Name) 26 | } 27 | 28 | err = c.WebsocketConn.WriteMessage(websocket.TextMessage, []byte(`{"messageType": "hello_world"}`)) 29 | 30 | if err != nil { 31 | log.Println(err) 32 | return 33 | } 34 | 35 | for c.Enabled && c.Websocket { 36 | msgType, resp, err := c.WebsocketConn.ReadMessage() 37 | if err != nil { 38 | log.Println(err) 39 | break 40 | } 41 | 42 | switch msgType { 43 | case websocket.TextMessage: 44 | type MsgType struct { 45 | MessageType string `json:"messageType"` 46 | } 47 | 48 | msgType := MsgType{} 49 | err := common.JSONDecode(resp, &msgType) 50 | if err != nil { 51 | log.Println(err) 52 | continue 53 | } 54 | log.Println(string(resp)) 55 | } 56 | } 57 | c.WebsocketConn.Close() 58 | log.Printf("%s Websocket client disconnected.", c.Name) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /orders.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | const ( 4 | limitOrder = iota 5 | marketOrder 6 | ) 7 | 8 | // Orders variable holds an array of pointers to order structs 9 | var Orders []*Order 10 | 11 | // Order struct holds order values 12 | type Order struct { 13 | OrderID int 14 | Exchange string 15 | Type int 16 | Amount float64 17 | Price float64 18 | } 19 | 20 | // NewOrder creates a new order and returns a an orderID 21 | func NewOrder(Exchange string, amount, price float64) int { 22 | order := &Order{} 23 | if len(Orders) == 0 { 24 | order.OrderID = 0 25 | } else { 26 | order.OrderID = len(Orders) 27 | } 28 | 29 | order.Exchange = Exchange 30 | order.Amount = amount 31 | order.Price = price 32 | Orders = append(Orders, order) 33 | return order.OrderID 34 | } 35 | 36 | // DeleteOrder deletes orders by ID and returns state 37 | func DeleteOrder(orderID int) bool { 38 | for i := range Orders { 39 | if Orders[i].OrderID == orderID { 40 | Orders = append(Orders[:i], Orders[i+1:]...) 41 | return true 42 | } 43 | } 44 | return false 45 | } 46 | 47 | // GetOrdersByExchange returns order pointer grouped by exchange 48 | func GetOrdersByExchange(exchange string) []*Order { 49 | orders := []*Order{} 50 | for i := range Orders { 51 | if Orders[i].Exchange == exchange { 52 | orders = append(orders, Orders[i]) 53 | } 54 | } 55 | if len(orders) > 0 { 56 | return orders 57 | } 58 | return nil 59 | } 60 | 61 | // GetOrderByOrderID returns order pointer by ID 62 | func GetOrderByOrderID(orderID int) *Order { 63 | for i := range Orders { 64 | if Orders[i].OrderID == orderID { 65 | return Orders[i] 66 | } 67 | } 68 | return nil 69 | } 70 | -------------------------------------------------------------------------------- /web/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit,ViewChild } from '@angular/core'; 2 | import { ElectronService } from './providers/electron.service'; 3 | import { MatSidenav } from '@angular/material'; 4 | import { SidebarService } from './services/sidebar/sidebar.service'; 5 | import { Router, NavigationEnd } from '@angular/router'; 6 | 7 | @Component({ 8 | selector: 'app-root', 9 | templateUrl: './app.component.html', 10 | styleUrls: ['./app.component.scss'], 11 | }) 12 | export class AppComponent { 13 | sidebarService: SidebarService 14 | public currentUrl:string; 15 | @ViewChild('sidenav') public sidenav: MatSidenav; 16 | 17 | constructor(public electronService: ElectronService,something: SidebarService, private router:Router) { 18 | 19 | if (electronService.isElectron()) { 20 | console.log('Mode electron'); 21 | // Check if electron is correctly injected (see externals in webpack.config.js) 22 | console.log('c', electronService.ipcRenderer); 23 | // Check if nodeJs childProcess is correctly injected (see externals in webpack.config.js) 24 | console.log('c', electronService.childProcess); 25 | } else { 26 | console.log('Mode web'); 27 | } 28 | 29 | this.sidebarService = something; 30 | 31 | router.events.subscribe(event => { 32 | 33 | if (event instanceof NavigationEnd ) { 34 | console.log("current url",event.url); // event.url has current url 35 | this.currentUrl = event.url; 36 | } 37 | }); 38 | } 39 | 40 | ngOnInit() { 41 | this.sidebarService.setSidenav(this.sidenav); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /web/package.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var packager = require('electron-packager'); 4 | const pkg = require('./package.json'); 5 | const argv = require('minimist')(process.argv.slice(1)); 6 | 7 | const appName = argv.name || pkg.name; 8 | const buildVersion = pkg.version || '1.0'; 9 | const shouldUseAsar = argv.asar || false; 10 | const shouldBuildAll = argv.all || false; 11 | const arch = argv.arch || 'all'; 12 | const platform = argv.platform || 'darwin'; 13 | 14 | const DEFAULT_OPTS = { 15 | dir: './dist', 16 | name: appName, 17 | asar: shouldUseAsar, 18 | buildVersion: buildVersion 19 | }; 20 | 21 | 22 | pack(platform, arch, function done(err, appPath) { 23 | if (err) { 24 | console.log(err); 25 | } else { 26 | console.log('Application packaged successfuly!', appPath); 27 | } 28 | 29 | }); 30 | 31 | function pack(plat, arch, cb) { 32 | // there is no darwin ia32 electron 33 | if (plat === 'darwin' && arch === 'ia32') return; 34 | 35 | let icon = 'src/favicon'; 36 | 37 | if (icon) { 38 | DEFAULT_OPTS.icon = icon + (() => { 39 | let extension = '.png'; 40 | if (plat === 'darwin') { 41 | extension = '.icns'; 42 | } else if (plat === 'win32') { 43 | extension = '.ico'; 44 | } 45 | return extension; 46 | })(); 47 | } 48 | 49 | const opts = Object.assign({}, DEFAULT_OPTS, { 50 | platform: plat, 51 | arch, 52 | prune: true, 53 | overwrite: true, 54 | all: shouldBuildAll, 55 | out: `app-builds` 56 | }); 57 | 58 | console.log(opts) 59 | packager(opts, cb); 60 | } 61 | -------------------------------------------------------------------------------- /web/src/app/pages/dashboard/dashboard.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, OnInit }from '@angular/core'; 2 | 3 | 4 | @Component( { 5 | selector:'app-dashboard', 6 | templateUrl:'./dashboard.component.html', 7 | styleUrls:['./dashboard.component.scss'], 8 | }) 9 | 10 | export class DashboardComponent implements OnInit { 11 | public dashboard:any; 12 | public expanded:boolean = false; 13 | 14 | constructor() { 15 | } 16 | 17 | ngOnInit() { 18 | this.resetTiles(); 19 | } 20 | 21 | public expandTile(tile:any) { 22 | for(var i = 0; i< this.dashboard.tiles.length; i++) { 23 | if(this.dashboard.tiles[i].title === tile.title ) { 24 | this.dashboard.tiles[i].rows = 2; 25 | this.dashboard.tiles[i].columns = 3; 26 | this.expanded = true; 27 | } else { 28 | this.dashboard.tiles[i].rows = 0; 29 | this.dashboard.tiles[i].columns = 0; 30 | } 31 | } 32 | } 33 | 34 | public resetTiles() { 35 | this.expanded = false; 36 | this.dashboard = {tiles:[ { 37 | title:'Trade History:', 38 | subTitle:'Trade History', 39 | content:'', 40 | columns:1, 41 | rows:2, 42 | }, { 43 | title:'Price History:', 44 | subTitle:'Price History', 45 | content:'', 46 | columns:2, 47 | rows:1, 48 | }, { 49 | title:'My Orders:', 50 | subTitle:'My Orders', 51 | content:'', 52 | columns:1, 53 | rows:1, 54 | }, { 55 | title:'Orders:', 56 | subTitle:'Orders', 57 | content:'', 58 | columns:1, 59 | rows:1, 60 | }, 61 | ]}; 62 | } 63 | } 64 | 65 | 66 | -------------------------------------------------------------------------------- /web/src/app/services/theme-storage/theme-storage.service.spec.ts: -------------------------------------------------------------------------------- 1 | import {ThemeStorageService} from './theme-storage.service'; 2 | 3 | 4 | const testStorageKey = ThemeStorageService.storageKey; 5 | const testTheme = { 6 | primary: '#000000', 7 | accent: '#ffffff', 8 | href: 'test/path/to/theme' 9 | }; 10 | const createTestData = () => { 11 | window.localStorage[testStorageKey] = JSON.stringify(testTheme); 12 | }; 13 | const clearTestData = () => { 14 | window.localStorage.clear(); 15 | }; 16 | 17 | describe('ThemeStorage Service', () => { 18 | const service = new ThemeStorageService(); 19 | const getCurrTheme = () => JSON.parse(window.localStorage.getItem(testStorageKey)); 20 | const secondTestTheme = { 21 | primary: '#666666', 22 | accent: '#333333', 23 | href: 'some/cool/path' 24 | }; 25 | 26 | beforeEach(createTestData); 27 | afterEach(clearTestData); 28 | 29 | it('should set the current theme', () => { 30 | expect(getCurrTheme()).toEqual(testTheme); 31 | service.storeTheme(secondTestTheme); 32 | expect(getCurrTheme()).toEqual(secondTestTheme); 33 | }); 34 | 35 | it('should get the current theme', () => { 36 | const theme = service.getStoredTheme(); 37 | expect(theme).toEqual(testTheme); 38 | }); 39 | 40 | it('should clear the stored theme data', () => { 41 | expect(getCurrTheme()).not.toBeNull(); 42 | service.clearStorage(); 43 | expect(getCurrTheme()).toBeNull(); 44 | }); 45 | 46 | it('should emit an event when setTheme is called', () => { 47 | spyOn(service.onThemeUpdate, 'emit'); 48 | service.storeTheme(secondTestTheme); 49 | expect(service.onThemeUpdate.emit).toHaveBeenCalled(); 50 | expect(service.onThemeUpdate.emit).toHaveBeenCalledWith(secondTestTheme); 51 | }); 52 | }); -------------------------------------------------------------------------------- /exchanges/alphapoint/alphapoint_websocket.go: -------------------------------------------------------------------------------- 1 | package alphapoint 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | 7 | "github.com/gorilla/websocket" 8 | "github.com/thrasher-/gocryptotrader/common" 9 | ) 10 | 11 | const ( 12 | alphapointDefaultWebsocketURL = "wss://sim3.alphapoint.com:8401/v1/GetTicker/" 13 | ) 14 | 15 | // WebsocketClient starts a new webstocket connection 16 | func (a *Alphapoint) WebsocketClient() { 17 | for a.Enabled && a.Websocket { 18 | var Dialer websocket.Dialer 19 | var err error 20 | a.WebsocketConn, _, err = Dialer.Dial(a.WebsocketURL, http.Header{}) 21 | 22 | if err != nil { 23 | log.Printf("%s Unable to connect to Websocket. Error: %s\n", a.Name, err) 24 | continue 25 | } 26 | 27 | if a.Verbose { 28 | log.Printf("%s Connected to Websocket.\n", a.Name) 29 | } 30 | 31 | err = a.WebsocketConn.WriteMessage(websocket.TextMessage, []byte(`{"messageType": "logon"}`)) 32 | 33 | if err != nil { 34 | log.Println(err) 35 | return 36 | } 37 | 38 | for a.Enabled && a.Websocket { 39 | msgType, resp, err := a.WebsocketConn.ReadMessage() 40 | if err != nil { 41 | log.Println(err) 42 | break 43 | } 44 | 45 | switch msgType { 46 | case websocket.TextMessage: 47 | type MsgType struct { 48 | MessageType string `json:"messageType"` 49 | } 50 | 51 | msgType := MsgType{} 52 | err := common.JSONDecode(resp, &msgType) 53 | if err != nil { 54 | log.Println(err) 55 | continue 56 | } 57 | 58 | switch msgType.MessageType { 59 | case "Ticker": 60 | ticker := WebsocketTicker{} 61 | err = common.JSONDecode(resp, &ticker) 62 | if err != nil { 63 | log.Println(err) 64 | continue 65 | } 66 | } 67 | } 68 | } 69 | a.WebsocketConn.Close() 70 | log.Printf("%s Websocket client disconnected.", a.Name) 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /exchanges/localbitcoins/localbitcoins_test.go: -------------------------------------------------------------------------------- 1 | package localbitcoins 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/thrasher-/gocryptotrader/config" 7 | ) 8 | 9 | var l LocalBitcoins 10 | 11 | // Please supply your own APIKEYS here for due diligence testing 12 | 13 | const ( 14 | apiKey = "" 15 | apiSecret = "" 16 | ) 17 | 18 | func TestSetDefaults(t *testing.T) { 19 | l.SetDefaults() 20 | } 21 | 22 | func TestSetup(t *testing.T) { 23 | cfg := config.GetConfig() 24 | cfg.LoadConfig("../../testdata/configtest.json") 25 | localbitcoinsConfig, err := cfg.GetExchangeConfig("LocalBitcoins") 26 | if err != nil { 27 | t.Error("Test Failed - LakeBTC Setup() init error") 28 | } 29 | 30 | localbitcoinsConfig.AuthenticatedAPISupport = true 31 | localbitcoinsConfig.APIKey = apiKey 32 | localbitcoinsConfig.APISecret = apiSecret 33 | 34 | l.Setup(localbitcoinsConfig) 35 | } 36 | 37 | func TestGetFee(t *testing.T) { 38 | t.Parallel() 39 | if l.GetFee(false) != 0 || l.GetFee(true) != 0 { 40 | t.Error("Test Failed - GetFee() error") 41 | } 42 | } 43 | 44 | func TestGetAccountInfo(t *testing.T) { 45 | t.Parallel() 46 | _, err := l.GetAccountInfo("", true) 47 | if err == nil { 48 | t.Error("Test Failed - GetAccountInfo() error", err) 49 | } 50 | _, err = l.GetAccountInfo("bitcoinbaron", false) 51 | if err != nil { 52 | t.Error("Test Failed - GetAccountInfo() error", err) 53 | } 54 | } 55 | 56 | func TestGetads(t *testing.T) { 57 | t.Parallel() 58 | _, err := l.Getads("") 59 | if err == nil { 60 | t.Error("Test Failed - Getads() - Full list, error", err) 61 | } 62 | _, err = l.Getads("1337") 63 | if err == nil { 64 | t.Error("Test Failed - Getads() error", err) 65 | } 66 | } 67 | 68 | func TestEditAd(t *testing.T) { 69 | t.Parallel() 70 | edit := AdEdit{} 71 | err := l.EditAd(edit, "1337") 72 | if err == nil { 73 | t.Error("Test Failed - EditAd() error", err) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /web/src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { HomeComponent } from './pages/home/home.component'; 2 | import { SettingsComponent } from './pages/settings/settings.component'; 3 | import { AboutComponent } from './pages/about/about.component'; 4 | import { DashboardComponent } from './pages/dashboard/dashboard.component'; 5 | import { WalletComponent } from './pages/wallet/wallet.component'; 6 | import { DonateComponent } from './pages/donate/donate.component'; 7 | import { HistoryComponent } from './pages/history/history.component'; 8 | import { TradingComponent } from './pages/trading/trading.component'; 9 | import { ExchangeGridComponent } from './pages/exchange-grid/exchange-grid.component'; 10 | import { CurrencyListComponent } from './pages/currency-list/currency-list.component'; 11 | 12 | import { NgModule } from '@angular/core'; 13 | import { Routes, RouterModule } from '@angular/router'; 14 | 15 | const routes: Routes = [ 16 | { 17 | path: '', 18 | component: HomeComponent 19 | }, 20 | { 21 | path:'about', 22 | component: AboutComponent 23 | }, 24 | { 25 | path:'dashboard', 26 | component: DashboardComponent 27 | }, 28 | { 29 | path: 'settings', 30 | component: SettingsComponent 31 | }, 32 | { 33 | path: 'wallet', 34 | component: WalletComponent 35 | } 36 | , 37 | { 38 | path: 'donate', 39 | component: DonateComponent 40 | }, 41 | { 42 | path: 'history', 43 | component: HistoryComponent 44 | }, 45 | { 46 | path: 'trading', 47 | component: TradingComponent 48 | }, 49 | { 50 | path: 'exchange-grid', 51 | component: ExchangeGridComponent 52 | }, 53 | { 54 | path: 'currency-list', 55 | component: CurrencyListComponent 56 | } 57 | 58 | ]; 59 | 60 | @NgModule({ 61 | imports: [RouterModule.forRoot(routes, {useHash: true})], 62 | exports: [RouterModule] 63 | }) 64 | export class AppRoutingModule { } 65 | -------------------------------------------------------------------------------- /web/main.ts: -------------------------------------------------------------------------------- 1 | import { app, BrowserWindow, screen } from 'electron'; 2 | import * as path from 'path'; 3 | 4 | 5 | let win, serve; 6 | const args = process.argv.slice(1); 7 | serve = args.some(val => val === '--serve'); 8 | 9 | if (serve) { 10 | require('electron-reload')(__dirname, { 11 | }); 12 | } 13 | 14 | function createWindow() { 15 | 16 | const electronScreen = screen; 17 | const size = electronScreen.getPrimaryDisplay().workAreaSize; 18 | 19 | // Create the browser window. 20 | win = new BrowserWindow({ 21 | x: 0, 22 | y: 0, 23 | width: size.width, 24 | height: size.height 25 | }); 26 | 27 | // and load the index.html of the app. 28 | win.loadURL('file://' + __dirname + '/index.html'); 29 | 30 | // Open the DevTools. 31 | if (serve) { 32 | win.webContents.openDevTools(); 33 | } 34 | 35 | // Emitted when the window is closed. 36 | win.on('closed', () => { 37 | // Dereference the window object, usually you would store window 38 | // in an array if your app supports multi windows, this is the time 39 | // when you should delete the corresponding element. 40 | win = null; 41 | }); 42 | } 43 | 44 | try { 45 | 46 | // This method will be called when Electron has finished 47 | // initialization and is ready to create browser windows. 48 | // Some APIs can only be used after this event occurs. 49 | app.on('ready', createWindow); 50 | 51 | // Quit when all windows are closed. 52 | app.on('window-all-closed', () => { 53 | // On OS X it is common for applications and their menu bar 54 | // to stay active until the user quits explicitly with Cmd + Q 55 | if (process.platform !== 'darwin') { 56 | app.quit(); 57 | } 58 | }); 59 | 60 | app.on('activate', () => { 61 | // On OS X it's common to re-create a window in the app when the 62 | // dock icon is clicked and there are no other windows open. 63 | if (win === null) { 64 | createWindow(); 65 | } 66 | }); 67 | 68 | } catch (e) { 69 | // Catch Error 70 | // throw e; 71 | } 72 | -------------------------------------------------------------------------------- /web/src/app/services/style-manager/style-manager.service.spec.ts: -------------------------------------------------------------------------------- 1 | import {inject, TestBed} from '@angular/core/testing'; 2 | import {HttpModule} from '@angular/http'; 3 | import {StyleManagerComponent} from './style-manager.component'; 4 | 5 | 6 | describe('StyleManager', () => { 7 | let styleManager: StyleManagerComponent; 8 | 9 | beforeEach(() => TestBed.configureTestingModule({ 10 | imports: [HttpModule], 11 | providers: [StyleManagerComponent] 12 | })); 13 | 14 | beforeEach(inject([StyleManagerComponent], (sm: StyleManagerComponent) => { 15 | styleManager = sm; 16 | })); 17 | 18 | afterEach(() => { 19 | let links = document.head.querySelectorAll('link'); 20 | for (let link of Array.prototype.slice.call(links)) { 21 | if (link.className.includes('style-manager-')) { 22 | document.head.removeChild(link); 23 | } 24 | } 25 | }); 26 | 27 | it('should add stylesheet to head', () => { 28 | styleManager.setStyle('test', 'test.css'); 29 | let styleEl = document.head.querySelector('.style-manager-test') as HTMLLinkElement; 30 | expect(styleEl).not.toBeNull(); 31 | expect(styleEl.href.endsWith('test.css')).toBe(true); 32 | }); 33 | 34 | it('should change existing stylesheet', () => { 35 | styleManager.setStyle('test', 'test.css'); 36 | let styleEl = document.head.querySelector('.style-manager-test') as HTMLLinkElement; 37 | expect(styleEl).not.toBeNull(); 38 | expect(styleEl.href.endsWith('test.css')).toBe(true); 39 | 40 | styleManager.setStyle('test', 'new.css'); 41 | expect(styleEl.href.endsWith('new.css')).toBe(true); 42 | }); 43 | 44 | it('should remove existing stylesheet', () => { 45 | styleManager.setStyle('test', 'test.css'); 46 | let styleEl = document.head.querySelector('.style-manager-test') as HTMLLinkElement; 47 | expect(styleEl).not.toBeNull(); 48 | expect(styleEl.href.endsWith('test.css')).toBe(true); 49 | 50 | styleManager.removeStyle('test'); 51 | styleEl = document.head.querySelector('.style-manager-test') as HTMLLinkElement; 52 | expect(styleEl).toBeNull(); 53 | }); 54 | }); -------------------------------------------------------------------------------- /exchanges/nonce/nonce.go: -------------------------------------------------------------------------------- 1 | package nonce 2 | 3 | import ( 4 | "strconv" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | // Nonce struct holds the nonce value 10 | type Nonce struct { 11 | // Standard nonce 12 | n int64 13 | mtx sync.Mutex 14 | // Hash table exclusive exchange specific nonce values 15 | boundedCall map[string]int64 16 | boundedMtx sync.Mutex 17 | } 18 | 19 | // Inc increments the nonce value 20 | func (n *Nonce) Inc() { 21 | n.mtx.Lock() 22 | n.n++ 23 | n.mtx.Unlock() 24 | } 25 | 26 | // Get retrives the nonce value 27 | func (n *Nonce) Get() int64 { 28 | n.mtx.Lock() 29 | defer n.mtx.Unlock() 30 | return n.n 31 | } 32 | 33 | // GetInc increments and returns the value of the nonce 34 | func (n *Nonce) GetInc() int64 { 35 | n.mtx.Lock() 36 | defer n.mtx.Unlock() 37 | n.n++ 38 | return n.n 39 | } 40 | 41 | // Set sets the nonce value 42 | func (n *Nonce) Set(val int64) { 43 | n.mtx.Lock() 44 | n.n = val 45 | n.mtx.Unlock() 46 | } 47 | 48 | // Returns a string version of the nonce 49 | func (n *Nonce) String() string { 50 | n.mtx.Lock() 51 | result := strconv.FormatInt(n.n, 10) 52 | n.mtx.Unlock() 53 | return result 54 | } 55 | 56 | // Value is a return type for GetValue 57 | type Value int64 58 | 59 | // GetValue returns a nonce value and can be set as a higher precision. Values 60 | // stored in an exchange specific hash table using a single locked call. 61 | func (n *Nonce) GetValue(exchName string, nanoPrecision bool) Value { 62 | n.boundedMtx.Lock() 63 | defer n.boundedMtx.Unlock() 64 | 65 | if n.boundedCall == nil { 66 | n.boundedCall = make(map[string]int64) 67 | } 68 | 69 | if n.boundedCall[exchName] == 0 { 70 | if nanoPrecision { 71 | n.boundedCall[exchName] = time.Now().UnixNano() 72 | return Value(n.boundedCall[exchName]) 73 | } 74 | n.boundedCall[exchName] = time.Now().Unix() 75 | return Value(n.boundedCall[exchName]) 76 | } 77 | n.boundedCall[exchName]++ 78 | return Value(n.boundedCall[exchName]) 79 | } 80 | 81 | // String is a Value method that changes format to a string 82 | func (v Value) String() string { 83 | return strconv.FormatInt(int64(v), 10) 84 | } 85 | -------------------------------------------------------------------------------- /web/src/app/shared/theme-picker/theme-picker.ts: -------------------------------------------------------------------------------- 1 | import {Component, ViewEncapsulation, ChangeDetectionStrategy, NgModule} from '@angular/core'; 2 | import { StyleManagerService } from './../../services/style-manager/style-manager.service'; 3 | 4 | import { ThemeStorageService,DocsSiteTheme } from './../../services/theme-storage/theme-storage.service'; 5 | import {CommonModule} from '@angular/common'; 6 | 7 | 8 | @Component({ 9 | selector: 'theme-picker', 10 | templateUrl: 'theme-picker.html', 11 | styleUrls: ['theme-picker.scss'], 12 | changeDetection: ChangeDetectionStrategy.OnPush, 13 | encapsulation: ViewEncapsulation.None, 14 | host: {'aria-hidden': 'true'}, 15 | }) 16 | export class ThemePickerComponent { 17 | currentTheme; 18 | 19 | themes = [ 20 | { 21 | primary: '#673AB7', 22 | accent: '#FFC107', 23 | href: 'deeppurple-amber.css', 24 | isDark: false, 25 | }, 26 | { 27 | primary: '#3F51B5', 28 | accent: '#E91E63', 29 | href: 'indigo-pink.css', 30 | isDark: false, 31 | isDefault: true, 32 | }, 33 | { 34 | primary: '#E91E63', 35 | accent: '#607D8B', 36 | href: 'pink-bluegrey.css', 37 | isDark: true, 38 | }, 39 | { 40 | primary: '#9C27B0', 41 | accent: '#4CAF50', 42 | href: 'purple-green.css', 43 | isDark: true, 44 | }, 45 | ]; 46 | 47 | constructor( 48 | public styleManager: StyleManagerService, 49 | private _themeStorage: ThemeStorageService 50 | ) { 51 | const currentTheme = this._themeStorage.getStoredTheme(); 52 | if (currentTheme) { 53 | this.installTheme(currentTheme); 54 | } 55 | } 56 | 57 | installTheme(theme: DocsSiteTheme) { 58 | this.currentTheme = this._getCurrentThemeFromHref(theme.href); 59 | 60 | if (theme.isDefault) { 61 | this.styleManager.removeStyle('theme'); 62 | } else { 63 | this.styleManager.setStyle('theme', `assets/${theme.href}`); 64 | } 65 | 66 | if (this.currentTheme) { 67 | this._themeStorage.storeTheme(this.currentTheme); 68 | } 69 | } 70 | 71 | private _getCurrentThemeFromHref(href: string): DocsSiteTheme { 72 | return this.themes.find(theme => theme.href === href); 73 | } 74 | } -------------------------------------------------------------------------------- /exchanges/poloniex/poloniex_test.go: -------------------------------------------------------------------------------- 1 | package poloniex 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/thrasher-/gocryptotrader/config" 7 | ) 8 | 9 | var p Poloniex 10 | 11 | // Please supply your own APIKEYS here for due diligence testing 12 | 13 | const ( 14 | apiKey = "" 15 | apiSecret = "" 16 | ) 17 | 18 | func TestSetDefaults(t *testing.T) { 19 | p.SetDefaults() 20 | } 21 | 22 | func TestSetup(t *testing.T) { 23 | cfg := config.GetConfig() 24 | cfg.LoadConfig("../../testdata/configtest.json") 25 | poloniexConfig, err := cfg.GetExchangeConfig("Poloniex") 26 | if err != nil { 27 | t.Error("Test Failed - Poloniex Setup() init error") 28 | } 29 | 30 | poloniexConfig.AuthenticatedAPISupport = true 31 | poloniexConfig.APIKey = apiKey 32 | poloniexConfig.APISecret = apiSecret 33 | 34 | p.Setup(poloniexConfig) 35 | } 36 | 37 | func TestGetFee(t *testing.T) { 38 | if p.GetFee() != 0 { 39 | t.Error("Test faild - Poloniex GetFee() error") 40 | } 41 | } 42 | 43 | func TestGetTicker(t *testing.T) { 44 | _, err := p.GetTicker() 45 | if err != nil { 46 | t.Error("Test faild - Poloniex GetTicker() error") 47 | } 48 | } 49 | 50 | func TestGetVolume(t *testing.T) { 51 | _, err := p.GetVolume() 52 | if err != nil { 53 | t.Error("Test faild - Poloniex GetVolume() error") 54 | } 55 | } 56 | 57 | func TestGetOrderbook(t *testing.T) { 58 | _, err := p.GetOrderbook("BTC_XMR", 50) 59 | if err != nil { 60 | t.Error("Test faild - Poloniex GetOrderbook() error", err) 61 | } 62 | } 63 | 64 | func TestGetTradeHistory(t *testing.T) { 65 | _, err := p.GetTradeHistory("BTC_XMR", "", "") 66 | if err != nil { 67 | t.Error("Test faild - Poloniex GetTradeHistory() error", err) 68 | } 69 | } 70 | 71 | func TestGetChartData(t *testing.T) { 72 | _, err := p.GetChartData("BTC_XMR", "1405699200", "1405699400", "300") 73 | if err != nil { 74 | t.Error("Test faild - Poloniex GetChartData() error", err) 75 | } 76 | } 77 | 78 | func TestGetCurrencies(t *testing.T) { 79 | _, err := p.GetCurrencies() 80 | if err != nil { 81 | t.Error("Test faild - Poloniex GetCurrencies() error", err) 82 | } 83 | } 84 | 85 | func TestGetLoanOrders(t *testing.T) { 86 | _, err := p.GetLoanOrders("BTC") 87 | if err != nil { 88 | t.Error("Test faild - Poloniex GetLoanOrders() error", err) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /exchanges/bitstamp/bitstamp_websocket.go: -------------------------------------------------------------------------------- 1 | package bitstamp 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/thrasher-/gocryptotrader/common" 7 | "github.com/toorop/go-pusher" 8 | ) 9 | 10 | // PusherOrderbook holds order book information to be pushed 11 | type PusherOrderbook struct { 12 | Asks [][]string `json:"asks"` 13 | Bids [][]string `json:"bids"` 14 | } 15 | 16 | // PusherTrade holds trade information to be pushed 17 | type PusherTrade struct { 18 | Price float64 `json:"price"` 19 | Amount float64 `json:"amount"` 20 | ID int64 `json:"id"` 21 | } 22 | 23 | const ( 24 | // BitstampPusherKey holds the current pusher key 25 | BitstampPusherKey = "de504dc5763aeef9ff52" 26 | ) 27 | 28 | // PusherClient starts the push mechanism 29 | func (b *Bitstamp) PusherClient() { 30 | for b.Enabled && b.Websocket { 31 | pusherClient, err := pusher.NewClient(BitstampPusherKey) 32 | if err != nil { 33 | log.Printf("%s Unable to connect to Websocket. Error: %s\n", b.GetName(), err) 34 | continue 35 | } 36 | 37 | err = pusherClient.Subscribe("live_trades") 38 | if err != nil { 39 | log.Printf("%s Websocket Trade subscription error: %s\n", b.GetName(), err) 40 | } 41 | 42 | err = pusherClient.Subscribe("order_book") 43 | if err != nil { 44 | log.Printf("%s Websocket Trade subscription error: %s\n", b.GetName(), err) 45 | } 46 | 47 | dataChannelTrade, err := pusherClient.Bind("data") 48 | if err != nil { 49 | log.Printf("%s Websocket Bind error: %s\n", b.GetName(), err) 50 | continue 51 | } 52 | tradeChannelTrade, err := pusherClient.Bind("trade") 53 | if err != nil { 54 | log.Printf("%s Websocket Bind error: %s\n", b.GetName(), err) 55 | continue 56 | } 57 | 58 | log.Printf("%s Pusher client connected.\n", b.GetName()) 59 | 60 | for b.Websocket { 61 | select { 62 | case data := <-dataChannelTrade: 63 | result := PusherOrderbook{} 64 | err := common.JSONDecode([]byte(data.Data), &result) 65 | if err != nil { 66 | log.Println(err) 67 | } 68 | case trade := <-tradeChannelTrade: 69 | result := PusherTrade{} 70 | err := common.JSONDecode([]byte(trade.Data), &result) 71 | if err != nil { 72 | log.Println(err) 73 | } 74 | log.Printf("%s Pusher trade: Price: %f Amount: %f\n", b.GetName(), result.Price, result.Amount) 75 | } 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /exchanges/btcc/btcc_test.go: -------------------------------------------------------------------------------- 1 | package btcc 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/thrasher-/gocryptotrader/config" 8 | ) 9 | 10 | // Please supply your own APIkeys here to do better tests 11 | const ( 12 | apiKey = "" 13 | apiSecret = "" 14 | ) 15 | 16 | var b BTCC 17 | 18 | func TestSetDefaults(t *testing.T) { 19 | b.SetDefaults() 20 | } 21 | 22 | func TestSetup(t *testing.T) { 23 | t.Parallel() 24 | b.Name = "BTCC" 25 | cfg := config.GetConfig() 26 | cfg.LoadConfig("../../testdata/configtest.json") 27 | bConfig, err := cfg.GetExchangeConfig("BTCC") 28 | if err != nil { 29 | t.Error("Test Failed - BTCC Setup() init error") 30 | } 31 | 32 | b.SetDefaults() 33 | b.Setup(bConfig) 34 | 35 | if !b.IsEnabled() || b.AuthenticatedAPISupport || b.RESTPollingDelay != time.Duration(10) || 36 | b.Verbose || b.Websocket || len(b.BaseCurrencies) < 1 || 37 | len(b.AvailablePairs) < 1 || len(b.EnabledPairs) < 1 { 38 | t.Error("Test Failed - BTCC Setup values not set correctly") 39 | } 40 | 41 | bConfig.Enabled = false 42 | b.Setup(bConfig) 43 | 44 | if b.IsEnabled() { 45 | t.Error("Test failed - BTCC TestSetup incorrect value") 46 | } 47 | } 48 | 49 | func TestGetFee(t *testing.T) { 50 | if b.GetFee() != 0 { 51 | t.Error("Test failed - GetFee() error") 52 | } 53 | } 54 | 55 | func TestGetTicker(t *testing.T) { 56 | _, err := b.GetTicker("ltccny") 57 | if err == nil { 58 | t.Error("Test failed - GetTicker() error", err) 59 | } 60 | } 61 | 62 | func TestGetTradesLast24h(t *testing.T) { 63 | _, err := b.GetTradesLast24h("ltccny") 64 | if err != nil { 65 | t.Error("Test failed - GetTradesLast24h() error", err) 66 | } 67 | } 68 | 69 | func TestGetTradeHistory(t *testing.T) { 70 | _, err := b.GetTradeHistory("ltccny", 0, 0, time.Time{}) 71 | if err != nil { 72 | t.Error("Test failed - GetTradeHistory() error", err) 73 | } 74 | } 75 | 76 | func TestGetOrderBook(t *testing.T) { 77 | b.Verbose = true 78 | _, err := b.GetOrderBook("ltccny", 100) 79 | if err == nil { 80 | t.Error("Test failed - GetOrderBook() error", err) 81 | } 82 | _, err = b.GetOrderBook("ltccny", 0) 83 | if err == nil { 84 | t.Error("Test failed - GetOrderBook() error", err) 85 | } 86 | } 87 | 88 | func TestGetAccountInfo(t *testing.T) { 89 | err := b.GetAccountInfo("") 90 | if err == nil { 91 | t.Error("Test failed - GetAccountInfo() error", err) 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /tools/config/config.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "log" 6 | 7 | "github.com/thrasher-/gocryptotrader/common" 8 | "github.com/thrasher-/gocryptotrader/config" 9 | ) 10 | 11 | // EncryptOrDecrypt returns a string from a boolean 12 | func EncryptOrDecrypt(encrypt bool) string { 13 | if encrypt { 14 | return "encrypted" 15 | } 16 | return "decrypted" 17 | } 18 | 19 | func main() { 20 | var inFile, outFile, key string 21 | var encrypt bool 22 | var err error 23 | configFile := config.GetFilePath("") 24 | flag.StringVar(&inFile, "infile", configFile, "The config input file to process.") 25 | flag.StringVar(&outFile, "outfile", configFile+".out", "The config output file.") 26 | flag.BoolVar(&encrypt, "encrypt", true, "Wether to encrypt or decrypt.") 27 | flag.StringVar(&key, "key", "", "The key to use for AES encryption.") 28 | flag.Parse() 29 | 30 | log.Println("GoCryptoTrader: config-helper tool.") 31 | 32 | if key == "" { 33 | result, errf := config.PromptForConfigKey() 34 | if errf != nil { 35 | log.Fatal("Unable to obtain encryption/decryption key.") 36 | } 37 | key = string(result) 38 | } 39 | 40 | file, err := common.ReadFile(inFile) 41 | if err != nil { 42 | log.Fatalf("Unable to read input file %s. Error: %s.", inFile, err) 43 | } 44 | 45 | if config.ConfirmECS(file) && encrypt { 46 | log.Println("File is already encrypted. Decrypting..") 47 | encrypt = false 48 | } 49 | 50 | if !config.ConfirmECS(file) && !encrypt { 51 | var result interface{} 52 | errf := config.ConfirmConfigJSON(file, result) 53 | if errf != nil { 54 | log.Fatal("File isn't in JSON format") 55 | } 56 | log.Println("File is already decrypted. Encrypting..") 57 | encrypt = true 58 | } 59 | 60 | var data []byte 61 | if encrypt { 62 | data, err = config.EncryptConfigFile(file, []byte(key)) 63 | if err != nil { 64 | log.Fatalf("Unable to encrypt config data. Error: %s.", err) 65 | } 66 | } else { 67 | data, err = config.DecryptConfigFile(file, []byte(key)) 68 | if err != nil { 69 | log.Fatalf("Unable to decrypt config data. Error: %s.", err) 70 | } 71 | } 72 | 73 | err = common.WriteFile(outFile, data) 74 | if err != nil { 75 | log.Fatalf("Unable to write output file %s. Error: %s", outFile, err) 76 | } 77 | log.Printf( 78 | "Successfully %s input file %s and wrote output to %s.\n", 79 | EncryptOrDecrypt(encrypt), inFile, outFile, 80 | ) 81 | } 82 | -------------------------------------------------------------------------------- /web/src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | view_quilt  8 |

Dashboard

9 |
10 | 11 | account_balance_wallet  12 |

Wallet

13 |
14 | 15 | swap_horiz  16 |

Trading

17 |
18 | 19 | history  20 |

History

21 |
22 | 23 | settings  24 |

Settings

25 |
26 | 27 | thumb_up  28 |

Donate

29 |
30 | 31 | 32 | grade  33 | 34 |

GitHub

35 |
36 |
37 | 38 | view_agenda  39 | 40 |

Trello

41 |
42 |
43 | 44 | apps  45 | 46 |

Slack

47 |
48 |
49 | 50 | bug_report  51 | 52 |

Report a bug

53 |
54 |
55 |
56 |
57 | 58 |
59 | -------------------------------------------------------------------------------- /currency/symbol/symbol.go: -------------------------------------------------------------------------------- 1 | package symbol 2 | 3 | import "errors" 4 | 5 | // symbols map holds the currency name and symbol mappings 6 | var symbols = map[string]string{ 7 | "ALL": "Lek", 8 | "AFN": "؋", 9 | "ARS": "$", 10 | "AWG": "ƒ", 11 | "AUD": "$", 12 | "AZN": "ман", 13 | "BSD": "$", 14 | "BBD": "$", 15 | "BYN": "Br", 16 | "BZD": "BZ$", 17 | "BMD": "$", 18 | "BOB": "$b", 19 | "BAM": "KM", 20 | "BWP": "P", 21 | "BGN": "лв", 22 | "BRL": "R$", 23 | "BND": "$", 24 | "KHR": "៛", 25 | "CAD": "$", 26 | "KYD": "$", 27 | "CLP": "$", 28 | "CNY": "¥", 29 | "COP": "$", 30 | "CRC": "₡", 31 | "HRK": "kn", 32 | "CUP": "₱", 33 | "CZK": "Kč", 34 | "DKK": "kr", 35 | "DOP": "RD$", 36 | "XCD": "$", 37 | "EGP": "£", 38 | "SVC": "$", 39 | "EUR": "€", 40 | "FKP": "£", 41 | "FJD": "$", 42 | "GHS": "¢", 43 | "GIP": "£", 44 | "GTQ": "Q", 45 | "GGP": "£", 46 | "GYD": "$", 47 | "HNL": "L", 48 | "HKD": "$", 49 | "HUF": "Ft", 50 | "ISK": "kr", 51 | "INR": "₹", 52 | "IDR": "Rp", 53 | "IRR": "﷼", 54 | "IMP": "£", 55 | "ILS": "₪", 56 | "JMD": "J$", 57 | "JPY": "¥", 58 | "JEP": "£", 59 | "KZT": "лв", 60 | "KPW": "₩", 61 | "KRW": "₩", 62 | "KGS": "лв", 63 | "LAK": "₭", 64 | "LBP": "£", 65 | "LRD": "$", 66 | "MKD": "ден", 67 | "MYR": "RM", 68 | "MUR": "₨", 69 | "MXN": "$", 70 | "MNT": "₮", 71 | "MZN": "MT", 72 | "NAD": "$", 73 | "NPR": "₨", 74 | "ANG": "ƒ", 75 | "NZD": "$", 76 | "NIO": "C$", 77 | "NGN": "₦", 78 | "NOK": "kr", 79 | "OMR": "﷼", 80 | "PKR": "₨", 81 | "PAB": "B/.", 82 | "PYG": "Gs", 83 | "PEN": "S/.", 84 | "PHP": "₱", 85 | "PLN": "zł", 86 | "QAR": "﷼", 87 | "RON": "lei", 88 | "RUB": "₽", 89 | "SHP": "£", 90 | "SAR": "﷼", 91 | "RSD": "Дин.", 92 | "SCR": "₨", 93 | "SGD": "$", 94 | "SBD": "$", 95 | "SOS": "S", 96 | "ZAR": "R", 97 | "LKR": "₨", 98 | "SEK": "kr", 99 | "CHF": "CHF", 100 | "SRD": "$", 101 | "SYP": "£", 102 | "TWD": "NT$", 103 | "THB": "฿", 104 | "TTD": "TT$", 105 | "TRY": "₺", 106 | "TVD": "$", 107 | "UAH": "₴", 108 | "GBP": "£", 109 | "USD": "$", 110 | "UYU": "$U", 111 | "UZS": "лв", 112 | "VEF": "Bs", 113 | "VND": "₫", 114 | "YER": "﷼", 115 | "ZWD": "Z$", 116 | } 117 | 118 | // GetSymbolByCurrencyName returns a currency symbol 119 | func GetSymbolByCurrencyName(currency string) (string, error) { 120 | result, ok := symbols[currency] 121 | if !ok { 122 | return "", errors.New("currency symbol not found") 123 | } 124 | return result, nil 125 | } 126 | -------------------------------------------------------------------------------- /web/src/app/pages/exchange-grid/exchange-grid.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Poloniex 6 | 7 | 8 | 9 | 10 | image and blurb 11 |
12 | 13 | 14 | attach_money 15 |

BTC_USD

16 | 17 |
18 | 19 | attach_money 20 |

LTC_USD

21 | 22 |
23 | 24 | attach_money 25 |

ETH_USD

26 | 27 |
28 |
29 |
30 |
31 | 32 | 33 | Kraken 34 | 35 | 36 | 37 | 38 | image and blurb 39 |
40 | 41 | 42 | 43 | attach_money 44 |

BTC_USD

45 | 46 |
47 | 48 | attach_money 49 |

LTC_USD

50 | 51 |
52 | 53 | attach_money 54 |

ETH_USD

55 | 56 |
57 |
58 |
59 |
60 |
-------------------------------------------------------------------------------- /exchanges/anx/anx_types.go: -------------------------------------------------------------------------------- 1 | package anx 2 | 3 | type ANXOrder struct { 4 | OrderType string `json:"orderType"` 5 | BuyTradedCurrency bool `json:"buyTradedCurrency"` 6 | TradedCurrency string `json:"tradedCurrency"` 7 | SettlementCurrency string `json:"settlementCurrency"` 8 | TradedCurrencyAmount string `json:"tradedCurrencyAmount"` 9 | SettlementCurrencyAmount string `json:"settlementCurrencyAmount"` 10 | LimitPriceInSettlementCurrency string `json:"limitPriceInSettlementCurrency"` 11 | ReplaceExistingOrderUUID string `json:"replaceExistingOrderUuid"` 12 | ReplaceOnlyIfActive bool `json:"replaceOnlyIfActive"` 13 | } 14 | 15 | type ANXOrderResponse struct { 16 | BuyTradedCurrency bool `json:"buyTradedCurrency"` 17 | ExecutedAverageRate string `json:"executedAverageRate"` 18 | LimitPriceInSettlementCurrency string `json:"limitPriceInSettlementCurrency"` 19 | OrderID string `json:"orderId"` 20 | OrderStatus string `json:"orderStatus"` 21 | OrderType string `json:"orderType"` 22 | ReplaceExistingOrderUUID string `json:"replaceExistingOrderId"` 23 | SettlementCurrency string `json:"settlementCurrency"` 24 | SettlementCurrencyAmount string `json:"settlementCurrencyAmount"` 25 | SettlementCurrencyOutstanding string `json:"settlementCurrencyOutstanding"` 26 | Timestamp int64 `json:"timestamp"` 27 | TradedCurrency string `json:"tradedCurrency"` 28 | TradedCurrencyAmount string `json:"tradedCurrencyAmount"` 29 | TradedCurrencyOutstanding string `json:"tradedCurrencyOutstanding"` 30 | } 31 | 32 | type ANXTickerComponent struct { 33 | Currency string `json:"currency"` 34 | Display string `json:"display"` 35 | DisplayShort string `json:"display_short"` 36 | Value string `json:"value"` 37 | } 38 | 39 | type ANXTicker struct { 40 | Result string `json:"result"` 41 | Data struct { 42 | High ANXTickerComponent `json:"high"` 43 | Low ANXTickerComponent `json:"low"` 44 | Avg ANXTickerComponent `json:"avg"` 45 | Vwap ANXTickerComponent `json:"vwap"` 46 | Vol ANXTickerComponent `json:"vol"` 47 | Last ANXTickerComponent `json:"last"` 48 | Buy ANXTickerComponent `json:"buy"` 49 | Sell ANXTickerComponent `json:"sell"` 50 | Now string `json:"now"` 51 | UpdateTime string `json:"dataUpdateTime"` 52 | } `json:"data"` 53 | } 54 | -------------------------------------------------------------------------------- /exchanges/nonce/nonce_test.go: -------------------------------------------------------------------------------- 1 | package nonce 2 | 3 | import ( 4 | "strconv" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func TestInc(t *testing.T) { 10 | var nonce Nonce 11 | nonce.Set(1) 12 | nonce.Inc() 13 | expected := int64(2) 14 | result := nonce.Get() 15 | if result != expected { 16 | t.Errorf("Test failed. Expected %d got %d", expected, result) 17 | } 18 | } 19 | 20 | func TestGet(t *testing.T) { 21 | var nonce Nonce 22 | nonce.Set(112321313) 23 | expected := int64(112321313) 24 | result := nonce.Get() 25 | if expected != result { 26 | t.Errorf("Test failed. Expected %d got %d", expected, result) 27 | } 28 | } 29 | 30 | func TestGetInc(t *testing.T) { 31 | var nonce Nonce 32 | nonce.Set(1) 33 | expected := int64(2) 34 | result := nonce.GetInc() 35 | if expected != result { 36 | t.Errorf("Test failed. Expected %d got %d", expected, result) 37 | } 38 | } 39 | 40 | func TestSet(t *testing.T) { 41 | var nonce Nonce 42 | nonce.Set(1) 43 | expected := int64(1) 44 | result := nonce.Get() 45 | if expected != result { 46 | t.Errorf("Test failed. Expected %d got %d", expected, result) 47 | } 48 | } 49 | 50 | func TestString(t *testing.T) { 51 | var nonce Nonce 52 | nonce.Set(12312313131) 53 | expected := "12312313131" 54 | result := nonce.String() 55 | if expected != result { 56 | t.Errorf("Test failed. Expected %s got %s", expected, result) 57 | } 58 | } 59 | 60 | func TestGetValue(t *testing.T) { 61 | var nonce Nonce 62 | timeNowNano := strconv.FormatInt(time.Now().UnixNano(), 10) 63 | time.Sleep(time.Millisecond * 100) 64 | nValue := nonce.GetValue("dingdong", true).String() 65 | 66 | if timeNowNano == nValue { 67 | t.Error("Test failed - GetValue() error, incorrect values") 68 | } 69 | 70 | if len(nValue) != 19 { 71 | t.Error("Test failed - GetValue() error, incorrect values") 72 | } 73 | 74 | timeNowUnix := nonce.GetValue("dongding", false) 75 | if len(timeNowUnix.String()) != 10 { 76 | t.Error("Test failed - GetValue() error, incorrect values") 77 | } 78 | 79 | n := nonce.GetValue("dongding", false) 80 | if n != timeNowUnix+1 { 81 | t.Error("Test failed - GetValue() error, incorrect values") 82 | } 83 | } 84 | 85 | func TestNonceConcurrency(t *testing.T) { 86 | var nonce Nonce 87 | nonce.Set(12312) 88 | 89 | for i := 0; i < 1000; i++ { 90 | go nonce.Inc() 91 | } 92 | 93 | // Allow sufficient time for all routines to finish 94 | time.Sleep(time.Second) 95 | 96 | result := nonce.Get() 97 | expected := int64(12312 + 1000) 98 | if expected != result { 99 | t.Errorf("Test failed. Expected %d got %d", expected, result) 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /web/src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE9, IE10 and IE11 requires all of the following polyfills. **/ 22 | // import 'core-js/es6/symbol'; 23 | // import 'core-js/es6/object'; 24 | // import 'core-js/es6/function'; 25 | // import 'core-js/es6/parse-int'; 26 | // import 'core-js/es6/parse-float'; 27 | // import 'core-js/es6/number'; 28 | // import 'core-js/es6/math'; 29 | // import 'core-js/es6/string'; 30 | // import 'core-js/es6/date'; 31 | // import 'core-js/es6/array'; 32 | // import 'core-js/es6/regexp'; 33 | // import 'core-js/es6/map'; 34 | // import 'core-js/es6/set'; 35 | 36 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 37 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 38 | 39 | /** IE10 and IE11 requires the following to support `@angular/animation`. */ 40 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 41 | 42 | 43 | /** Evergreen browsers require these. **/ 44 | import 'core-js/es6/reflect'; 45 | import 'core-js/es7/reflect'; 46 | 47 | 48 | /** ALL Firefox browsers require the following to support `@angular/animation`. **/ 49 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 50 | 51 | 52 | 53 | /*************************************************************************************************** 54 | * Zone JS is required by Angular itself. 55 | */ 56 | import 'zone.js/dist/zone-mix'; // Included with Angular CLI. 57 | 58 | 59 | /*************************************************************************************************** 60 | * APPLICATION IMPORTS 61 | */ 62 | 63 | /** 64 | * Date, currency, decimal and percent pipes. 65 | * Needed for: All but Chrome, Firefox, Edge, IE11 and Safari 10 66 | */ 67 | // import 'intl'; // Run `npm install --save intl`. 68 | -------------------------------------------------------------------------------- /web/src/app/shared/all-enabled-currency-tickers/all-enabled-currency-tickers.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { WebsocketHandlerService } from './../../services/websocket-handler/websocket-handler.service'; 3 | 4 | @Component({ 5 | selector: 'app-all-enabled-currency-tickers', 6 | templateUrl: './all-enabled-currency-tickers.component.html', 7 | styleUrls: ['./all-enabled-currency-tickers.component.scss'] 8 | }) 9 | export class AllEnabledCurrencyTickersComponent implements OnInit { 10 | private ws: WebsocketHandlerService; 11 | allCurrencies:ExchangeCurrency[]; 12 | tickerCards: TickerUpdate[]; 13 | 14 | constructor(private websocketHandler: WebsocketHandlerService) { 15 | this.ws = websocketHandler; 16 | this.allCurrencies = []; 17 | this.tickerCards = []; 18 | this.ws.messages.subscribe(msg => { 19 | if (msg.Event === 'ticker_update') { 20 | var modal = {}; 21 | modal.currencyPair = msg.data.CurrencyPair; 22 | modal.exchangeName = msg.Exchange; 23 | var found = false; 24 | 25 | for(var i = 0; i< this.allCurrencies.length; i++) { 26 | if(this.allCurrencies[i].currencyPair === msg.data.CurrencyPair && 27 | this.allCurrencies[i].exchangeName === msg.Exchange) { 28 | found = true; 29 | } 30 | } 31 | if(!found) { 32 | //time to add 33 | var ticker = msg.data; 34 | ticker.Exchange = msg.Exchange; 35 | this.tickerCards.push(ticker); 36 | this.allCurrencies.push(modal); 37 | } else { 38 | //time to replace 39 | for(var j = 0; j< this.tickerCards.length; j++) { 40 | if(this.tickerCards[j].Exchange === msg.Exchange 41 | && this.tickerCards[j].CurrencyPair === msg.data.CurrencyPair) { 42 | var ticker = msg.data; 43 | this.tickerCards[j] = ticker; 44 | this.tickerCards[j].Exchange = msg.Exchange; 45 | return; 46 | } 47 | } 48 | } 49 | } 50 | }); 51 | } 52 | ngOnInit() { } 53 | } 54 | 55 | export interface ExchangeCurrency { 56 | currencyPair: string; 57 | exchangeName:string; 58 | } 59 | 60 | export interface CurrencyPair { 61 | delimiter: string; 62 | first_currency: string; 63 | second_currency: string; 64 | } 65 | 66 | export interface TickerUpdate { 67 | Pair: CurrencyPair; 68 | CurrencyPair: string; 69 | Last: number; 70 | High: number; 71 | Low: number; 72 | Bid: number; 73 | Ask: number; 74 | Volume: number; 75 | PriceATH: number; 76 | Exchange:string; 77 | } -------------------------------------------------------------------------------- /web/README.md: -------------------------------------------------------------------------------- 1 | # GoCryptoTrader Website 2 | 3 | 4 | 5 | A website interface to interact with the main GoCryptoTrader application. It is developed with Angular 4 with support for Electron 6 | 7 | ## This is still in active development 8 | 9 | You can track ideas, planned features and what's in progresss on this Trello board: [https://trello.com/b/ZAhMhpOy/gocryptotrader](https://trello.com/b/ZAhMhpOy/gocryptotrader). 10 | 11 | ## Current Features 12 | 13 | + It can run 14 | + It can be compiled with Electron to run as an executable 15 | + Websocket support to listen to GoCryptoTrader events 16 | + Material design 17 | + Has a semi-working Settings page 18 | + Has a basic ticker dashboard 19 | 20 | ## Install dependencies with npm 21 | 22 | ``` bash 23 | npm install 24 | ``` 25 | 26 | If you want to generate Angular components with Angular-cli , you **MUST** install `@angular/cli` in npm global context. 27 | Please follow [Angular-cli documentation](https://github.com/angular/angular-cli) if you had installed a previous version of `angular-cli`. 28 | 29 | ``` bash 30 | npm install -g @angular/cli 31 | ``` 32 | 33 | ## To build for development 34 | 35 | ``` bash 36 | npm run start:web 37 | ``` 38 | 39 | Voila! You can use GoCryptoTrader web app in a local development environment with webpack watching! 40 | 41 | ## To build for production 42 | 43 | + Using development variables (environments/index.ts) : `npm run electron:dev` 44 | + Using production variables (environments/index.prod.ts) : `npm run electron:prod` 45 | 46 | Your built files are in the /dist folder. 47 | 48 | ## Included Commands 49 | 50 | |Command|Description| 51 | |--|--| 52 | |`npm run start:web`| Execute the app in the brower | 53 | |`npm run electron:linux`| Builds your application and creates an app consumable on linux system | 54 | |`npm run electron:windows`| On a Windows OS, builds your application and creates an app consumable in windows 32/64 bit systems | 55 | |`npm run electron:mac`| On a MAC OS, builds your application and generates a `.app` file of your application that can be run on Ma | 56 | 57 | ## Execute E2E tests 58 | 59 | You can find end-to-end tests in /e2e folder. 60 | 61 | You can run tests with the command lines below: 62 | 63 | + **in a terminal window** -> First, start a web server on port 4200 : `npm run start:web` 64 | + **in another terminal window** -> Then, launch Protractor (E2E framework): `npm run e2e` 65 | 66 | ## Contributors 67 | 68 | |User|Github|Contribution| 69 | |--|--|--| 70 | |GloriousCode|https://github.com/gloriouscode |Lead front-end| 71 | |Maxime GRIS|https://github.com/maximegris |Angular4 + Electron Base| 72 | |Shazbert|https://github.com/shazbert |Initial designs| -------------------------------------------------------------------------------- /config/config_encryption_test.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | 7 | "github.com/thrasher-/gocryptotrader/common" 8 | ) 9 | 10 | func TestPromptForConfigEncryption(t *testing.T) { 11 | t.Parallel() 12 | 13 | if Cfg.PromptForConfigEncryption() { 14 | t.Error("Test failed. PromptForConfigEncryption return incorrect bool") 15 | } 16 | } 17 | 18 | func TestPromptForConfigKey(t *testing.T) { 19 | t.Parallel() 20 | 21 | byteyBite, err := PromptForConfigKey() 22 | if err == nil && len(byteyBite) > 1 { 23 | t.Errorf("Test failed. PromptForConfigKey: %s", err) 24 | } 25 | } 26 | 27 | func TestEncryptDecryptConfigFile(t *testing.T) { //Dual function Test 28 | testKey := []byte("12345678901234567890123456789012") 29 | 30 | testConfigData, err := common.ReadFile(ConfigTestFile) 31 | if err != nil { 32 | t.Errorf("Test failed. EncryptConfigFile: %s", err) 33 | } 34 | encryptedFile, err2 := EncryptConfigFile(testConfigData, testKey) 35 | if err2 != nil { 36 | t.Errorf("Test failed. EncryptConfigFile: %s", err2) 37 | } 38 | if reflect.TypeOf(encryptedFile).String() != "[]uint8" { 39 | t.Errorf("Test failed. EncryptConfigFile: Incorrect Type") 40 | } 41 | 42 | decryptedFile, err3 := DecryptConfigFile(encryptedFile, testKey) 43 | if err3 != nil { 44 | t.Errorf("Test failed. DecryptConfigFile: %s", err3) 45 | } 46 | if reflect.TypeOf(decryptedFile).String() != "[]uint8" { 47 | t.Errorf("Test failed. DecryptConfigFile: Incorrect Type") 48 | } 49 | // unmarshalled := Config{} // racecondition 50 | // err4 := json.Unmarshal(decryptedFile, &unmarshalled) 51 | // if err4 != nil { 52 | // t.Errorf("Test failed. DecryptConfigFile: %s", err3) 53 | // } 54 | } 55 | 56 | func TestConfirmConfigJSON(t *testing.T) { 57 | var result interface{} 58 | testConfirmJSON, err := common.ReadFile(ConfigTestFile) 59 | if err != nil { 60 | t.Errorf("Test failed. testConfirmJSON: %s", err) 61 | } 62 | 63 | err2 := ConfirmConfigJSON(testConfirmJSON, &result) 64 | if err2 != nil { 65 | t.Errorf("Test failed. testConfirmJSON: %s", err2) 66 | } 67 | if result == nil { 68 | t.Errorf("Test failed. testConfirmJSON: Error Unmarshalling JSON") 69 | } 70 | err3 := ConfirmConfigJSON(testConfirmJSON, result) 71 | if err3 == nil { 72 | t.Errorf("Test failed. testConfirmJSON: %s", err3) 73 | } 74 | } 75 | 76 | func TestConfirmECS(t *testing.T) { 77 | t.Parallel() 78 | 79 | ECStest := []byte(EncryptConfirmString) 80 | if !ConfirmECS(ECStest) { 81 | t.Errorf("Test failed. TestConfirmECS: Error finding ECS.") 82 | } 83 | } 84 | 85 | func TestRemoveECS(t *testing.T) { 86 | t.Parallel() 87 | 88 | ECStest := []byte(EncryptConfirmString) 89 | isremoved := RemoveECS(ECStest) 90 | 91 | if string(isremoved) != "" { 92 | t.Errorf("Test failed. TestConfirmECS: Error ECS not deleted.") 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /exchanges/kraken/kraken_types.go: -------------------------------------------------------------------------------- 1 | package kraken 2 | 3 | // GeneralResponse is a generalized response type 4 | type GeneralResponse struct { 5 | Result map[string]interface{} `json:"result"` 6 | Error []interface{} `json:"error"` 7 | } 8 | 9 | // AssetPairs holds asset pair information 10 | type AssetPairs struct { 11 | Altname string `json:"altname"` 12 | AclassBase string `json:"aclass_base"` 13 | Base string `json:"base"` 14 | AclassQuote string `json:"aclass_quote"` 15 | Quote string `json:"quote"` 16 | Lot string `json:"lot"` 17 | PairDecimals int `json:"pair_decimals"` 18 | LotDecimals int `json:"lot_decimals"` 19 | LotMultiplier int `json:"lot_multiplier"` 20 | LeverageBuy []int `json:"leverage_buy"` 21 | LeverageSell []int `json:"leverage_sell"` 22 | Fees [][]float64 `json:"fees"` 23 | FeesMaker [][]float64 `json:"fees_maker"` 24 | FeeVolumeCurrency string `json:"fee_volume_currency"` 25 | MarginCall int `json:"margin_call"` 26 | MarginStop int `json:"margin_stop"` 27 | } 28 | 29 | // Ticker is a standard ticker type 30 | type Ticker struct { 31 | Ask float64 32 | Bid float64 33 | Last float64 34 | Volume float64 35 | VWAP float64 36 | Trades int64 37 | Low float64 38 | High float64 39 | Open float64 40 | } 41 | 42 | // TickerResponse holds ticker information before its put into the Ticker struct 43 | type TickerResponse struct { 44 | Ask []string `json:"a"` 45 | Bid []string `json:"b"` 46 | Last []string `json:"c"` 47 | Volume []string `json:"v"` 48 | VWAP []string `json:"p"` 49 | Trades []int64 `json:"t"` 50 | Low []string `json:"l"` 51 | High []string `json:"h"` 52 | Open string `json:"o"` 53 | } 54 | 55 | // OpenHighLowClose contains ticker event information 56 | type OpenHighLowClose struct { 57 | Time float64 58 | Open float64 59 | High float64 60 | Low float64 61 | Close float64 62 | Vwap float64 63 | Volume float64 64 | Count float64 65 | } 66 | 67 | // RecentTrades holds recent trade data 68 | type RecentTrades struct { 69 | Price float64 70 | Volume float64 71 | Time float64 72 | BuyOrSell string 73 | MarketOrLimit string 74 | Miscellaneous interface{} 75 | } 76 | 77 | // OrderbookBase stores the orderbook price and amount data 78 | type OrderbookBase struct { 79 | Price float64 80 | Amount float64 81 | } 82 | 83 | // Orderbook stores the bids and asks orderbook data 84 | type Orderbook struct { 85 | Bids []OrderbookBase 86 | Asks []OrderbookBase 87 | } 88 | 89 | // Spread holds the spread between trades 90 | type Spread struct { 91 | Time float64 92 | Bid float64 93 | Ask float64 94 | } 95 | -------------------------------------------------------------------------------- /web/src/app/pages/wallet/wallet.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { WebsocketHandlerService } from './../../services/websocket-handler/websocket-handler.service'; 3 | import { Wallet, CoinTotal } from './../../shared/classes/wallet'; 4 | import {Sort} from '@angular/material'; 5 | 6 | @Component({ 7 | selector: 'app-wallet', 8 | templateUrl: './wallet.component.html', 9 | styleUrls: ['./wallet.component.scss'] 10 | }) 11 | 12 | export class WalletComponent implements OnInit { 13 | private ws: WebsocketHandlerService; 14 | private failCount = 0; 15 | private timer: any; 16 | public wallet: Wallet; 17 | displayedColumns = ['coin', 'balance']; 18 | 19 | private getWalletMessage = { 20 | Event: 'GetPortfolio', 21 | data: null, 22 | }; 23 | 24 | constructor(private websocketHandler: WebsocketHandlerService) { 25 | this.wallet= null; 26 | this.ws = websocketHandler; 27 | this.ws.messages.subscribe(msg => { 28 | if (msg.Event === 'GetPortfolio') { 29 | console.log(JSON.stringify(msg.data)); 30 | this.wallet = msg.data; 31 | 32 | this.attachIcon(this.wallet.coin_totals); 33 | this.attachIcon(this.wallet.coins_offline); 34 | this.attachIcon(this.wallet.coins_online); 35 | 36 | this.attachIcon(this.wallet.offline_summary.BTC); 37 | this.attachIcon(this.wallet.offline_summary.ETH); 38 | this.attachIcon(this.wallet.offline_summary.LTC); 39 | 40 | this.attachIcon(this.wallet.online_summary.BTC); 41 | this.attachIcon(this.wallet.online_summary.ETH); 42 | this.attachIcon(this.wallet.online_summary.LTC); 43 | } 44 | }); 45 | } 46 | 47 | public coinIcon(coin:string) :string { 48 | switch(coin) { 49 | case "BTC": return "attach_money"; 50 | case "LTC": return "attach_money"; 51 | case "ETH": return "attach_money"; 52 | } 53 | } 54 | 55 | public attachIcon(items: CoinTotal[]): void { 56 | if (items) { 57 | for (var i = 0; i < items.length; i++) { 58 | items[i].icon = this.coinIcon(items[i].coin); 59 | } 60 | } 61 | } 62 | 63 | ngOnInit() { 64 | this.setWallet(); 65 | } 66 | 67 | //there has to be a better way 68 | private resendMessageIfPageRefreshed(): void { 69 | if (this.failCount <= 10) { 70 | setTimeout(() => { 71 | if (this.wallet === null || this.wallet === undefined) { 72 | this.failCount++; 73 | this.setWallet(); 74 | } 75 | }, 1000); 76 | } else { 77 | console.log('Could not load wallet. Check if GocryptoTrader server is running, otherwise open a ticket'); 78 | } 79 | } 80 | 81 | private setWallet():void { 82 | this.ws.messages.next(this.getWalletMessage); 83 | this.resendMessageIfPageRefreshed(); 84 | } 85 | } 86 | 87 | 88 | -------------------------------------------------------------------------------- /exchanges/bitfinex/bitfinex_websocket_test.go: -------------------------------------------------------------------------------- 1 | package bitfinex 2 | 3 | import ( 4 | "net/http" 5 | "testing" 6 | 7 | "github.com/gorilla/websocket" 8 | ) 9 | 10 | func TestWebsocketPingHandler(t *testing.T) { 11 | wsPingHandler := Bitfinex{} 12 | var Dialer websocket.Dialer 13 | var err error 14 | 15 | wsPingHandler.WebsocketConn, _, err = Dialer.Dial(bitfinexWebsocket, http.Header{}) 16 | if err != nil { 17 | t.Errorf("Test Failed - Bitfinex dialer error: %s", err) 18 | } 19 | err = wsPingHandler.WebsocketPingHandler() 20 | if err != nil { 21 | t.Errorf("Test Failed - Bitfinex WebsocketPingHandler() error: %s", err) 22 | } 23 | err = wsPingHandler.WebsocketConn.Close() 24 | if err != nil { 25 | t.Errorf("Test Failed - Bitfinex websocketConn.Close() error: %s", err) 26 | } 27 | } 28 | 29 | func TestWebsocketSubscribe(t *testing.T) { 30 | websocketSubcribe := Bitfinex{} 31 | var Dialer websocket.Dialer 32 | var err error 33 | params := make(map[string]string) 34 | params["pair"] = "BTCUSD" 35 | 36 | websocketSubcribe.WebsocketConn, _, err = Dialer.Dial(bitfinexWebsocket, http.Header{}) 37 | if err != nil { 38 | t.Errorf("Test Failed - Bitfinex Dialer error: %s", err) 39 | } 40 | err = websocketSubcribe.WebsocketSubscribe("ticker", params) 41 | if err != nil { 42 | t.Errorf("Test Failed - Bitfinex WebsocketSubscribe() error: %s", err) 43 | } 44 | 45 | err = websocketSubcribe.WebsocketConn.Close() 46 | if err != nil { 47 | t.Errorf("Test Failed - Bitfinex websocketConn.Close() error: %s", err) 48 | } 49 | } 50 | 51 | func TestWebsocketSendAuth(t *testing.T) { 52 | wsSendAuth := Bitfinex{} 53 | var Dialer websocket.Dialer 54 | var err error 55 | 56 | wsSendAuth.WebsocketConn, _, err = Dialer.Dial(bitfinexWebsocket, http.Header{}) 57 | if err != nil { 58 | t.Errorf("Test Failed - Bitfinex Dialer error: %s", err) 59 | } 60 | err = wsSendAuth.WebsocketSendAuth() 61 | if err != nil { 62 | t.Errorf("Test Failed - Bitfinex WebsocketSendAuth() error: %s", err) 63 | } 64 | } 65 | 66 | func TestWebsocketAddSubscriptionChannel(t *testing.T) { 67 | wsAddSubscriptionChannel := Bitfinex{} 68 | wsAddSubscriptionChannel.SetDefaults() 69 | var Dialer websocket.Dialer 70 | var err error 71 | 72 | wsAddSubscriptionChannel.WebsocketConn, _, err = Dialer.Dial(bitfinexWebsocket, http.Header{}) 73 | if err != nil { 74 | t.Errorf("Test Failed - Bitfinex Dialer error: %s", err) 75 | } 76 | 77 | wsAddSubscriptionChannel.WebsocketAddSubscriptionChannel(1337, "ticker", "BTCUSD") 78 | if len(wsAddSubscriptionChannel.WebsocketSubdChannels) == 0 { 79 | t.Errorf("Test Failed - Bitfinex WebsocketAddSubscriptionChannel() error: %s", err) 80 | } 81 | if wsAddSubscriptionChannel.WebsocketSubdChannels[1337].Channel != "ticker" { 82 | t.Errorf("Test Failed - Bitfinex WebsocketAddSubscriptionChannel() error: %s", err) 83 | } 84 | if wsAddSubscriptionChannel.WebsocketSubdChannels[1337].Pair != "BTCUSD" { 85 | t.Errorf("Test Failed - Bitfinex WebsocketAddSubscriptionChannel() error: %s", err) 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /exchanges/lakebtc/lakebtc_test.go: -------------------------------------------------------------------------------- 1 | package lakebtc 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/thrasher-/gocryptotrader/config" 7 | ) 8 | 9 | var l LakeBTC 10 | 11 | // Please add your own APIkeys to do correct due diligence testing. 12 | const ( 13 | apiKey = "" 14 | apiSecret = "" 15 | ) 16 | 17 | func TestSetDefaults(t *testing.T) { 18 | l.SetDefaults() 19 | } 20 | 21 | func TestSetup(t *testing.T) { 22 | cfg := config.GetConfig() 23 | cfg.LoadConfig("../../testdata/configtest.json") 24 | lakebtcConfig, err := cfg.GetExchangeConfig("LakeBTC") 25 | if err != nil { 26 | t.Error("Test Failed - LakeBTC Setup() init error") 27 | } 28 | 29 | lakebtcConfig.AuthenticatedAPISupport = true 30 | lakebtcConfig.APIKey = apiKey 31 | lakebtcConfig.APISecret = apiSecret 32 | 33 | l.Setup(lakebtcConfig) 34 | } 35 | 36 | func TestGetFee(t *testing.T) { 37 | t.Parallel() 38 | if l.GetFee(false) != 0.2 { 39 | t.Error("Test Failed - GetFee() error") 40 | } 41 | if l.GetFee(true) != 0.15 { 42 | t.Error("Test Failed - GetFee() error") 43 | } 44 | } 45 | 46 | func TestGetTicker(t *testing.T) { 47 | t.Parallel() 48 | _, err := l.GetTicker() 49 | if err != nil { 50 | t.Error("Test Failed - GetTicker() error", err) 51 | } 52 | } 53 | 54 | func TestGetOrderBook(t *testing.T) { 55 | t.Parallel() 56 | _, err := l.GetOrderBook("BTCUSD") 57 | if err != nil { 58 | t.Error("Test Failed - GetOrderBook() error", err) 59 | } 60 | } 61 | 62 | func TestGetTradeHistory(t *testing.T) { 63 | t.Parallel() 64 | _, err := l.GetTradeHistory("BTCUSD") 65 | if err != nil { 66 | t.Error("Test Failed - GetTradeHistory() error", err) 67 | } 68 | } 69 | 70 | func TestTrade(t *testing.T) { 71 | t.Parallel() 72 | _, err := l.Trade(0, 0, 0, "USD") 73 | if err == nil { 74 | t.Error("Test Failed - Trade() error", err) 75 | } 76 | } 77 | 78 | func TestGetOpenOrders(t *testing.T) { 79 | t.Parallel() 80 | _, err := l.GetOpenOrders() 81 | if err == nil { 82 | t.Error("Test Failed - GetOpenOrders() error", err) 83 | } 84 | } 85 | 86 | func TestGetOrders(t *testing.T) { 87 | t.Parallel() 88 | _, err := l.GetOrders([]int64{1, 2}) 89 | if err == nil { 90 | t.Error("Test Failed - GetOrders() error", err) 91 | } 92 | } 93 | 94 | func TestCancelOrder(t *testing.T) { 95 | t.Parallel() 96 | err := l.CancelOrder(1337) 97 | if err == nil { 98 | t.Error("Test Failed - CancelOrder() error", err) 99 | } 100 | } 101 | 102 | func TestGetTrades(t *testing.T) { 103 | t.Parallel() 104 | _, err := l.GetTrades(1337) 105 | if err == nil { 106 | t.Error("Test Failed - GetTrades() error", err) 107 | } 108 | } 109 | 110 | func TestGetExternalAccounts(t *testing.T) { 111 | t.Parallel() 112 | _, err := l.GetExternalAccounts() 113 | if err == nil { 114 | t.Error("Test Failed - GetExternalAccounts() error", err) 115 | } 116 | } 117 | 118 | func TestCreateWithdraw(t *testing.T) { 119 | t.Parallel() 120 | _, err := l.CreateWithdraw(0, 1337) 121 | if err == nil { 122 | t.Error("Test Failed - CreateWithdraw() error", err) 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /restful_router.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net/http" 7 | "time" 8 | 9 | "github.com/gorilla/mux" 10 | "github.com/thrasher-/gocryptotrader/exchanges" 11 | ) 12 | 13 | // RESTLogger logs the requests internally 14 | func RESTLogger(inner http.Handler, name string) http.Handler { 15 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 16 | start := time.Now() 17 | 18 | inner.ServeHTTP(w, r) 19 | 20 | log.Printf( 21 | "%s\t%s\t%s\t%s", 22 | r.Method, 23 | r.RequestURI, 24 | name, 25 | time.Since(start), 26 | ) 27 | }) 28 | } 29 | 30 | // Route is a sub type that holds the request routes 31 | type Route struct { 32 | Name string 33 | Method string 34 | Pattern string 35 | HandlerFunc http.HandlerFunc 36 | } 37 | 38 | // Routes is an array of all the registered routes 39 | type Routes []Route 40 | 41 | var routes = Routes{} 42 | 43 | // NewRouter takes in the exchange interfaces and returns a new multiplexor 44 | // router 45 | func NewRouter(exchanges []exchange.IBotExchange) *mux.Router { 46 | router := mux.NewRouter().StrictSlash(true) 47 | 48 | routes = Routes{ 49 | Route{ 50 | "", 51 | "GET", 52 | "/", 53 | getIndex, 54 | }, 55 | Route{ 56 | "GetAllSettings", 57 | "GET", 58 | "/config/all", 59 | RESTGetAllSettings, 60 | }, 61 | Route{ 62 | "SaveAllSettings", 63 | "POST", 64 | "/config/all/save", 65 | RESTSaveAllSettings, 66 | }, 67 | Route{ 68 | "AllEnabledAccountInfo", 69 | "GET", 70 | "/exchanges/enabled/accounts/all", 71 | RESTGetAllEnabledAccountInfo, 72 | }, 73 | Route{ 74 | "AllActiveExchangesAndCurrencies", 75 | "GET", 76 | "/exchanges/enabled/latest/all", 77 | RESTGetAllActiveTickers, 78 | }, 79 | Route{ 80 | "IndividualExchangeAndCurrency", 81 | "GET", 82 | "/exchanges/{exchangeName}/latest/{currency}", 83 | RESTGetTicker, 84 | }, 85 | Route{ 86 | "GetPortfolio", 87 | "GET", 88 | "/portfolio/all", 89 | RESTGetPortfolio, 90 | }, 91 | Route{ 92 | "AllActiveExchangesAndOrderbooks", 93 | "GET", 94 | "/exchanges/orderbook/latest/all", 95 | RESTGetAllActiveOrderbooks, 96 | }, 97 | Route{ 98 | "IndividualExchangeOrderbook", 99 | "GET", 100 | "/exchanges/{exchangeName}/orderbook/latest/{currency}", 101 | RESTGetOrderbook, 102 | }, 103 | Route{ 104 | "ws", 105 | "GET", 106 | "/ws", 107 | WebsocketClientHandler, 108 | }, 109 | } 110 | 111 | for _, route := range routes { 112 | var handler http.Handler 113 | handler = route.HandlerFunc 114 | handler = RESTLogger(handler, route.Name) 115 | 116 | router. 117 | Methods(route.Method). 118 | Path(route.Pattern). 119 | Name(route.Name). 120 | Handler(handler) 121 | } 122 | return router 123 | } 124 | 125 | func getIndex(w http.ResponseWriter, r *http.Request) { 126 | fmt.Fprint(w, "GoCryptoTrader RESTful interface. For the web GUI, please visit the web GUI readme.") 127 | w.WriteHeader(http.StatusOK) 128 | } 129 | -------------------------------------------------------------------------------- /exchanges/liqui/liqui_test.go: -------------------------------------------------------------------------------- 1 | package liqui 2 | 3 | import ( 4 | "net/url" 5 | "testing" 6 | 7 | "github.com/thrasher-/gocryptotrader/config" 8 | ) 9 | 10 | var l Liqui 11 | 12 | const ( 13 | apiKey = "" 14 | apiSecret = "" 15 | ) 16 | 17 | func TestSetDefaults(t *testing.T) { 18 | l.SetDefaults() 19 | } 20 | 21 | func TestSetup(t *testing.T) { 22 | cfg := config.GetConfig() 23 | cfg.LoadConfig("../../testdata/configtest.json") 24 | liquiConfig, err := cfg.GetExchangeConfig("Liqui") 25 | if err != nil { 26 | t.Error("Test Failed - liqui Setup() init error") 27 | } 28 | 29 | liquiConfig.AuthenticatedAPISupport = true 30 | liquiConfig.APIKey = apiKey 31 | liquiConfig.APISecret = apiSecret 32 | 33 | l.Setup(liquiConfig) 34 | } 35 | 36 | func TestGetFee(t *testing.T) { 37 | _, err := l.GetFee("usd") 38 | if err == nil { 39 | t.Error("Test Failed - liqui GetFee() error", err) 40 | } 41 | } 42 | 43 | func TestGetAvailablePairs(t *testing.T) { 44 | v := l.GetAvailablePairs(false) 45 | if len(v) != 0 { 46 | t.Error("Test Failed - liqui GetFee() error") 47 | } 48 | } 49 | 50 | func TestGetInfo(t *testing.T) { 51 | _, err := l.GetInfo() 52 | if err != nil { 53 | t.Error("Test Failed - liqui GetInfo() error", err) 54 | } 55 | } 56 | 57 | func TestGetTicker(t *testing.T) { 58 | _, err := l.GetTicker("eth_btc") 59 | if err != nil { 60 | t.Error("Test Failed - liqui GetTicker() error", err) 61 | } 62 | } 63 | 64 | func TestGetDepth(t *testing.T) { 65 | _, err := l.GetDepth("eth_btc") 66 | if err != nil { 67 | t.Error("Test Failed - liqui GetDepth() error", err) 68 | } 69 | } 70 | 71 | func TestGetTrades(t *testing.T) { 72 | _, err := l.GetTrades("eth_btc") 73 | if err != nil { 74 | t.Error("Test Failed - liqui GetTrades() error", err) 75 | } 76 | } 77 | 78 | func TestGetAccountInfo(t *testing.T) { 79 | _, err := l.GetAccountInfo() 80 | if err == nil { 81 | t.Error("Test Failed - liqui GetAccountInfo() error", err) 82 | } 83 | } 84 | 85 | func TestTrade(t *testing.T) { 86 | _, err := l.Trade("", "", 0, 1) 87 | if err == nil { 88 | t.Error("Test Failed - liqui Trade() error", err) 89 | } 90 | } 91 | 92 | func TestGetActiveOrders(t *testing.T) { 93 | _, err := l.GetActiveOrders("eth_btc") 94 | if err == nil { 95 | t.Error("Test Failed - liqui GetActiveOrders() error", err) 96 | } 97 | } 98 | 99 | func TestGetOrderInfo(t *testing.T) { 100 | _, err := l.GetOrderInfo(1337) 101 | if err == nil { 102 | t.Error("Test Failed - liqui GetOrderInfo() error", err) 103 | } 104 | } 105 | 106 | func TestCancelOrder(t *testing.T) { 107 | _, err := l.CancelOrder(1337) 108 | if err == nil { 109 | t.Error("Test Failed - liqui CancelOrder() error", err) 110 | } 111 | } 112 | 113 | func TestGetTradeHistory(t *testing.T) { 114 | _, err := l.GetTradeHistory(url.Values{}, "") 115 | if err == nil { 116 | t.Error("Test Failed - liqui GetTradeHistory() error", err) 117 | } 118 | } 119 | 120 | func TestWithdrawCoins(t *testing.T) { 121 | _, err := l.WithdrawCoins("btc", 1337, "someaddr") 122 | if err == nil { 123 | t.Error("Test Failed - liqui WithdrawCoins() error", err) 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /exchanges/gdax/gdax_websocket.go: -------------------------------------------------------------------------------- 1 | package gdax 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | 7 | "github.com/gorilla/websocket" 8 | "github.com/thrasher-/gocryptotrader/common" 9 | ) 10 | 11 | const ( 12 | GDAX_WEBSOCKET_URL = "wss://ws-feed.gdax.com" 13 | ) 14 | 15 | func (g *GDAX) WebsocketSubscribe(product string, conn *websocket.Conn) error { 16 | subscribe := WebsocketSubscribe{"subscribe", product} 17 | json, err := common.JSONEncode(subscribe) 18 | if err != nil { 19 | return err 20 | } 21 | 22 | err = conn.WriteMessage(websocket.TextMessage, json) 23 | 24 | if err != nil { 25 | return err 26 | } 27 | return nil 28 | } 29 | 30 | func (g *GDAX) WebsocketClient() { 31 | for g.Enabled && g.Websocket { 32 | var Dialer websocket.Dialer 33 | conn, _, err := Dialer.Dial(GDAX_WEBSOCKET_URL, http.Header{}) 34 | 35 | if err != nil { 36 | log.Printf("%s Unable to connect to Websocket. Error: %s\n", g.GetName(), err) 37 | continue 38 | } 39 | 40 | log.Printf("%s Connected to Websocket.\n", g.GetName()) 41 | 42 | currencies := []string{} 43 | for _, x := range g.EnabledPairs { 44 | currency := x[0:3] + "-" + x[3:] 45 | currencies = append(currencies, currency) 46 | } 47 | 48 | for _, x := range currencies { 49 | err = g.WebsocketSubscribe(x, conn) 50 | if err != nil { 51 | log.Printf("%s Websocket subscription error: %s\n", g.GetName(), err) 52 | continue 53 | } 54 | } 55 | 56 | if g.Verbose { 57 | log.Printf("%s Subscribed to product messages.", g.GetName()) 58 | } 59 | 60 | for g.Enabled && g.Websocket { 61 | msgType, resp, err := conn.ReadMessage() 62 | if err != nil { 63 | log.Println(err) 64 | break 65 | } 66 | 67 | switch msgType { 68 | case websocket.TextMessage: 69 | type MsgType struct { 70 | Type string `json:"type"` 71 | } 72 | 73 | msgType := MsgType{} 74 | err := common.JSONDecode(resp, &msgType) 75 | if err != nil { 76 | log.Println(err) 77 | continue 78 | } 79 | 80 | switch msgType.Type { 81 | case "error": 82 | log.Println(string(resp)) 83 | break 84 | case "received": 85 | received := WebsocketReceived{} 86 | err := common.JSONDecode(resp, &received) 87 | if err != nil { 88 | log.Println(err) 89 | continue 90 | } 91 | case "open": 92 | open := WebsocketOpen{} 93 | err := common.JSONDecode(resp, &open) 94 | if err != nil { 95 | log.Println(err) 96 | continue 97 | } 98 | case "done": 99 | done := WebsocketDone{} 100 | err := common.JSONDecode(resp, &done) 101 | if err != nil { 102 | log.Println(err) 103 | continue 104 | } 105 | case "match": 106 | match := WebsocketMatch{} 107 | err := common.JSONDecode(resp, &match) 108 | if err != nil { 109 | log.Println(err) 110 | continue 111 | } 112 | case "change": 113 | change := WebsocketChange{} 114 | err := common.JSONDecode(resp, &change) 115 | if err != nil { 116 | log.Println(err) 117 | continue 118 | } 119 | } 120 | } 121 | } 122 | conn.Close() 123 | log.Printf("%s Websocket client disconnected.", g.GetName()) 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /web/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": [ 3 | "node_modules/codelyzer" 4 | ], 5 | "rules": { 6 | "callable-types": true, 7 | "class-name": true, 8 | "comment-format": [ 9 | true, 10 | "check-space" 11 | ], 12 | "curly": true, 13 | "eofline": true, 14 | "forin": true, 15 | "import-blacklist": [true, "rxjs"], 16 | "import-spacing": true, 17 | "indent": [ 18 | true, 19 | "spaces" 20 | ], 21 | "interface-over-type-literal": true, 22 | "label-position": true, 23 | "max-line-length": [ 24 | true, 25 | 140 26 | ], 27 | "member-access": false, 28 | "member-ordering": [ 29 | true, 30 | "static-before-instance", 31 | "variables-before-functions" 32 | ], 33 | "no-arg": true, 34 | "no-bitwise": true, 35 | "no-console": [ 36 | true, 37 | "debug", 38 | "info", 39 | "time", 40 | "timeEnd", 41 | "trace" 42 | ], 43 | "no-construct": true, 44 | "no-debugger": true, 45 | "no-duplicate-variable": true, 46 | "no-empty": false, 47 | "no-empty-interface": true, 48 | "no-eval": true, 49 | "no-inferrable-types": [true, "ignore-params"], 50 | "no-shadowed-variable": true, 51 | "no-string-literal": false, 52 | "no-string-throw": true, 53 | "no-switch-case-fall-through": true, 54 | "no-trailing-whitespace": true, 55 | "no-unused-expression": true, 56 | "no-use-before-declare": true, 57 | "no-var-keyword": true, 58 | "object-literal-sort-keys": false, 59 | "one-line": [ 60 | true, 61 | "check-open-brace", 62 | "check-catch", 63 | "check-else", 64 | "check-whitespace" 65 | ], 66 | "prefer-const": true, 67 | "quotemark": [ 68 | true, 69 | "single" 70 | ], 71 | "radix": true, 72 | "semicolon": [ 73 | "always" 74 | ], 75 | "triple-equals": [ 76 | true, 77 | "allow-null-check" 78 | ], 79 | "typedef-whitespace": [ 80 | true, 81 | { 82 | "call-signature": "nospace", 83 | "index-signature": "nospace", 84 | "parameter": "nospace", 85 | "property-declaration": "nospace", 86 | "variable-declaration": "nospace" 87 | } 88 | ], 89 | "typeof-compare": true, 90 | "unified-signatures": true, 91 | "variable-name": false, 92 | "whitespace": [ 93 | true, 94 | "check-branch", 95 | "check-decl", 96 | "check-operator", 97 | "check-separator", 98 | "check-type" 99 | ], 100 | 101 | "directive-selector": [true, "attribute", "app", "camelCase"], 102 | "component-selector": [true, "element", "app", "kebab-case"], 103 | "use-input-property-decorator": true, 104 | "use-output-property-decorator": true, 105 | "use-host-property-decorator": true, 106 | "no-input-rename": true, 107 | "no-output-rename": true, 108 | "use-life-cycle-interface": true, 109 | "use-pipe-transform-interface": true, 110 | "component-class-suffix": true, 111 | "directive-class-suffix": true, 112 | "no-access-missing-member": true, 113 | "templates-use-public": true, 114 | "invoke-injectable": true 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /exchanges/alphapoint/alphapoint_wrapper.go: -------------------------------------------------------------------------------- 1 | package alphapoint 2 | 3 | import ( 4 | "github.com/thrasher-/gocryptotrader/currency/pair" 5 | "github.com/thrasher-/gocryptotrader/exchanges" 6 | "github.com/thrasher-/gocryptotrader/exchanges/orderbook" 7 | "github.com/thrasher-/gocryptotrader/exchanges/ticker" 8 | ) 9 | 10 | // GetExchangeAccountInfo retrieves balances for all enabled currencies on the 11 | // Alphapoint exchange 12 | func (a *Alphapoint) GetExchangeAccountInfo() (exchange.AccountInfo, error) { 13 | var response exchange.AccountInfo 14 | response.ExchangeName = a.GetName() 15 | account, err := a.GetAccountInfo() 16 | if err != nil { 17 | return response, err 18 | } 19 | for i := 0; i < len(account.Currencies); i++ { 20 | var exchangeCurrency exchange.AccountCurrencyInfo 21 | exchangeCurrency.CurrencyName = account.Currencies[i].Name 22 | exchangeCurrency.TotalValue = float64(account.Currencies[i].Balance) 23 | exchangeCurrency.Hold = float64(account.Currencies[i].Hold) 24 | 25 | response.Currencies = append(response.Currencies, exchangeCurrency) 26 | } 27 | //If it all works out 28 | return response, nil 29 | } 30 | 31 | // UpdateTicker updates and returns the ticker for a currency pair 32 | func (a *Alphapoint) UpdateTicker(p pair.CurrencyPair, assetType string) (ticker.Price, error) { 33 | var tickerPrice ticker.Price 34 | tick, err := a.GetTicker(p.Pair().String()) 35 | if err != nil { 36 | return tickerPrice, err 37 | } 38 | 39 | tickerPrice.Pair = p 40 | tickerPrice.Ask = tick.Ask 41 | tickerPrice.Bid = tick.Bid 42 | tickerPrice.Low = tick.Low 43 | tickerPrice.High = tick.High 44 | tickerPrice.Volume = tick.Volume 45 | tickerPrice.Last = tick.Last 46 | ticker.ProcessTicker(a.GetName(), p, tickerPrice, assetType) 47 | return ticker.GetTicker(a.Name, p, assetType) 48 | } 49 | 50 | // GetTickerPrice returns the ticker for a currency pair 51 | func (a *Alphapoint) GetTickerPrice(p pair.CurrencyPair, assetType string) (ticker.Price, error) { 52 | tick, err := ticker.GetTicker(a.GetName(), p, assetType) 53 | if err != nil { 54 | return a.UpdateTicker(p, assetType) 55 | } 56 | return tick, nil 57 | } 58 | 59 | // UpdateOrderbook updates and returns the orderbook for a currency pair 60 | func (a *Alphapoint) UpdateOrderbook(p pair.CurrencyPair, assetType string) (orderbook.Base, error) { 61 | var orderBook orderbook.Base 62 | orderbookNew, err := a.GetOrderbook(p.Pair().String()) 63 | if err != nil { 64 | return orderBook, err 65 | } 66 | 67 | for x := range orderbookNew.Bids { 68 | data := orderbookNew.Bids[x] 69 | orderBook.Bids = append(orderBook.Bids, orderbook.Item{Amount: data.Quantity, Price: data.Price}) 70 | } 71 | 72 | for x := range orderbookNew.Asks { 73 | data := orderbookNew.Asks[x] 74 | orderBook.Asks = append(orderBook.Asks, orderbook.Item{Amount: data.Quantity, Price: data.Price}) 75 | } 76 | 77 | orderbook.ProcessOrderbook(a.GetName(), p, orderBook, assetType) 78 | return orderbook.GetOrderbook(a.Name, p, assetType) 79 | } 80 | 81 | // GetOrderbookEx returns the orderbook for a currency pair 82 | func (a *Alphapoint) GetOrderbookEx(p pair.CurrencyPair, assetType string) (orderbook.Base, error) { 83 | ob, err := orderbook.GetOrderbook(a.GetName(), p, assetType) 84 | if err == nil { 85 | return a.UpdateOrderbook(p, assetType) 86 | } 87 | return ob, nil 88 | } 89 | -------------------------------------------------------------------------------- /exchanges/btcc/btcc_websocket.go: -------------------------------------------------------------------------------- 1 | package btcc 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | 7 | "github.com/thrasher-/gocryptotrader/common" 8 | "github.com/thrasher-/socketio" 9 | ) 10 | 11 | const ( 12 | BTCC_SOCKETIO_ADDRESS = "https://websocket.btcc.com" 13 | ) 14 | 15 | var BTCCSocket *socketio.SocketIO 16 | 17 | func (b *BTCC) OnConnect(output chan socketio.Message) { 18 | if b.Verbose { 19 | log.Printf("%s Connected to Websocket.", b.GetName()) 20 | } 21 | 22 | currencies := []string{} 23 | for _, x := range b.EnabledPairs { 24 | currency := common.StringToLower(x[3:] + x[0:3]) 25 | currencies = append(currencies, currency) 26 | } 27 | endpoints := []string{"marketdata", "grouporder"} 28 | 29 | for _, x := range endpoints { 30 | for _, y := range currencies { 31 | channel := fmt.Sprintf(`"%s_%s"`, x, y) 32 | if b.Verbose { 33 | log.Printf("%s Websocket subscribing to channel: %s.", b.GetName(), channel) 34 | } 35 | output <- socketio.CreateMessageEvent("subscribe", channel, b.OnMessage, BTCCSocket.Version) 36 | } 37 | } 38 | } 39 | 40 | func (b *BTCC) OnDisconnect(output chan socketio.Message) { 41 | log.Printf("%s Disconnected from websocket server.. Reconnecting.\n", b.GetName()) 42 | b.WebsocketClient() 43 | } 44 | 45 | func (b *BTCC) OnError() { 46 | log.Printf("%s Error with Websocket connection.. Reconnecting.\n", b.GetName()) 47 | b.WebsocketClient() 48 | } 49 | 50 | func (b *BTCC) OnMessage(message []byte, output chan socketio.Message) { 51 | if b.Verbose { 52 | log.Printf("%s Websocket message received which isn't handled by default.\n", b.GetName()) 53 | log.Println(string(message)) 54 | } 55 | } 56 | 57 | func (b *BTCC) OnTicker(message []byte, output chan socketio.Message) { 58 | type Response struct { 59 | Ticker WebsocketTicker `json:"ticker"` 60 | } 61 | var resp Response 62 | err := common.JSONDecode(message, &resp) 63 | 64 | if err != nil { 65 | log.Println(err) 66 | return 67 | } 68 | } 69 | 70 | func (b *BTCC) OnGroupOrder(message []byte, output chan socketio.Message) { 71 | type Response struct { 72 | GroupOrder WebsocketGroupOrder `json:"grouporder"` 73 | } 74 | var resp Response 75 | err := common.JSONDecode(message, &resp) 76 | 77 | if err != nil { 78 | log.Println(err) 79 | return 80 | } 81 | } 82 | 83 | func (b *BTCC) OnTrade(message []byte, output chan socketio.Message) { 84 | trade := WebsocketTrade{} 85 | err := common.JSONDecode(message, &trade) 86 | 87 | if err != nil { 88 | log.Println(err) 89 | return 90 | } 91 | } 92 | 93 | func (b *BTCC) WebsocketClient() { 94 | events := make(map[string]func(message []byte, output chan socketio.Message)) 95 | events["grouporder"] = b.OnGroupOrder 96 | events["ticker"] = b.OnTicker 97 | events["trade"] = b.OnTrade 98 | 99 | BTCCSocket = &socketio.SocketIO{ 100 | Version: 1, 101 | OnConnect: b.OnConnect, 102 | OnEvent: events, 103 | OnError: b.OnError, 104 | OnMessage: b.OnMessage, 105 | OnDisconnect: b.OnDisconnect, 106 | } 107 | 108 | for b.Enabled && b.Websocket { 109 | err := socketio.ConnectToSocket(BTCC_SOCKETIO_ADDRESS, BTCCSocket) 110 | if err != nil { 111 | log.Printf("%s Unable to connect to Websocket. Err: %s\n", b.GetName(), err) 112 | continue 113 | } 114 | log.Printf("%s Disconnected from Websocket.\n", b.GetName()) 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /exchanges/huobi/huobi_wrapper.go: -------------------------------------------------------------------------------- 1 | package huobi 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/thrasher-/gocryptotrader/common" 7 | "github.com/thrasher-/gocryptotrader/currency/pair" 8 | "github.com/thrasher-/gocryptotrader/exchanges" 9 | "github.com/thrasher-/gocryptotrader/exchanges/orderbook" 10 | "github.com/thrasher-/gocryptotrader/exchanges/ticker" 11 | ) 12 | 13 | // Start starts the HUOBI go routine 14 | func (h *HUOBI) Start() { 15 | go h.Run() 16 | } 17 | 18 | // Run implements the HUOBI wrapper 19 | func (h *HUOBI) Run() { 20 | if h.Verbose { 21 | log.Printf("%s Websocket: %s (url: %s).\n", h.GetName(), common.IsEnabled(h.Websocket), HUOBI_SOCKETIO_ADDRESS) 22 | log.Printf("%s polling delay: %ds.\n", h.GetName(), h.RESTPollingDelay) 23 | log.Printf("%s %d currencies enabled: %s.\n", h.GetName(), len(h.EnabledPairs), h.EnabledPairs) 24 | } 25 | 26 | if h.Websocket { 27 | go h.WebsocketClient() 28 | } 29 | } 30 | 31 | // UpdateTicker updates and returns the ticker for a currency pair 32 | func (h *HUOBI) UpdateTicker(p pair.CurrencyPair, assetType string) (ticker.Price, error) { 33 | var tickerPrice ticker.Price 34 | tick, err := h.GetTicker(p.GetFirstCurrency().Lower().String()) 35 | if err != nil { 36 | return tickerPrice, err 37 | } 38 | tickerPrice.Pair = p 39 | tickerPrice.Ask = tick.Sell 40 | tickerPrice.Bid = tick.Buy 41 | tickerPrice.Low = tick.Low 42 | tickerPrice.Last = tick.Last 43 | tickerPrice.Volume = tick.Vol 44 | tickerPrice.High = tick.High 45 | ticker.ProcessTicker(h.GetName(), p, tickerPrice, assetType) 46 | return ticker.GetTicker(h.Name, p, assetType) 47 | } 48 | 49 | // GetTickerPrice returns the ticker for a currency pair 50 | func (h *HUOBI) GetTickerPrice(p pair.CurrencyPair, assetType string) (ticker.Price, error) { 51 | tickerNew, err := ticker.GetTicker(h.GetName(), p, assetType) 52 | if err != nil { 53 | return h.UpdateTicker(p, assetType) 54 | } 55 | return tickerNew, nil 56 | } 57 | 58 | // GetOrderbookEx returns orderbook base on the currency pair 59 | func (h *HUOBI) GetOrderbookEx(p pair.CurrencyPair, assetType string) (orderbook.Base, error) { 60 | ob, err := orderbook.GetOrderbook(h.GetName(), p, assetType) 61 | if err == nil { 62 | return h.UpdateOrderbook(p, assetType) 63 | } 64 | return ob, nil 65 | } 66 | 67 | // UpdateOrderbook updates and returns the orderbook for a currency pair 68 | func (h *HUOBI) UpdateOrderbook(p pair.CurrencyPair, assetType string) (orderbook.Base, error) { 69 | var orderBook orderbook.Base 70 | orderbookNew, err := h.GetOrderBook(p.GetFirstCurrency().Lower().String()) 71 | if err != nil { 72 | return orderBook, err 73 | } 74 | 75 | for x := range orderbookNew.Bids { 76 | data := orderbookNew.Bids[x] 77 | orderBook.Bids = append(orderBook.Bids, orderbook.Item{Amount: data[1], Price: data[0]}) 78 | } 79 | 80 | for x := range orderbookNew.Asks { 81 | data := orderbookNew.Asks[x] 82 | orderBook.Asks = append(orderBook.Asks, orderbook.Item{Amount: data[1], Price: data[0]}) 83 | } 84 | 85 | orderbook.ProcessOrderbook(h.GetName(), p, orderBook, assetType) 86 | return orderbook.GetOrderbook(h.Name, p, assetType) 87 | } 88 | 89 | //GetExchangeAccountInfo retrieves balances for all enabled currencies for the 90 | // HUOBI exchange - to-do 91 | func (h *HUOBI) GetExchangeAccountInfo() (exchange.AccountInfo, error) { 92 | var response exchange.AccountInfo 93 | response.ExchangeName = h.GetName() 94 | return response, nil 95 | } 96 | -------------------------------------------------------------------------------- /exchanges/btcc/btcc_wrapper.go: -------------------------------------------------------------------------------- 1 | package btcc 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/thrasher-/gocryptotrader/common" 7 | "github.com/thrasher-/gocryptotrader/currency/pair" 8 | exchange "github.com/thrasher-/gocryptotrader/exchanges" 9 | "github.com/thrasher-/gocryptotrader/exchanges/orderbook" 10 | "github.com/thrasher-/gocryptotrader/exchanges/ticker" 11 | ) 12 | 13 | // Start starts the BTCC go routine 14 | func (b *BTCC) Start() { 15 | go b.Run() 16 | } 17 | 18 | // Run implements the BTCC wrapper 19 | func (b *BTCC) Run() { 20 | if b.Verbose { 21 | log.Printf("%s Websocket: %s.", b.GetName(), common.IsEnabled(b.Websocket)) 22 | log.Printf("%s polling delay: %ds.\n", b.GetName(), b.RESTPollingDelay) 23 | log.Printf("%s %d currencies enabled: %s.\n", b.GetName(), len(b.EnabledPairs), b.EnabledPairs) 24 | } 25 | 26 | if b.Websocket { 27 | go b.WebsocketClient() 28 | } 29 | } 30 | 31 | // UpdateTicker updates and returns the ticker for a currency pair 32 | func (b *BTCC) UpdateTicker(p pair.CurrencyPair, assetType string) (ticker.Price, error) { 33 | var tickerPrice ticker.Price 34 | tick, err := b.GetTicker(exchange.FormatExchangeCurrency(b.GetName(), p).String()) 35 | if err != nil { 36 | return tickerPrice, err 37 | } 38 | tickerPrice.Pair = p 39 | tickerPrice.Ask = tick.Sell 40 | tickerPrice.Bid = tick.Buy 41 | tickerPrice.Low = tick.Low 42 | tickerPrice.Last = tick.Last 43 | tickerPrice.Volume = tick.Vol 44 | tickerPrice.High = tick.High 45 | ticker.ProcessTicker(b.GetName(), p, tickerPrice, assetType) 46 | return ticker.GetTicker(b.Name, p, assetType) 47 | } 48 | 49 | // GetTickerPrice returns the ticker for a currency pair 50 | func (b *BTCC) GetTickerPrice(p pair.CurrencyPair, assetType string) (ticker.Price, error) { 51 | tickerNew, err := ticker.GetTicker(b.GetName(), p, assetType) 52 | if err != nil { 53 | return b.UpdateTicker(p, assetType) 54 | } 55 | return tickerNew, nil 56 | } 57 | 58 | // GetOrderbookEx returns the orderbook for a currency pair 59 | func (b *BTCC) GetOrderbookEx(p pair.CurrencyPair, assetType string) (orderbook.Base, error) { 60 | ob, err := orderbook.GetOrderbook(b.GetName(), p, assetType) 61 | if err == nil { 62 | return b.UpdateOrderbook(p, assetType) 63 | } 64 | return ob, nil 65 | } 66 | 67 | // UpdateOrderbook updates and returns the orderbook for a currency pair 68 | func (b *BTCC) UpdateOrderbook(p pair.CurrencyPair, assetType string) (orderbook.Base, error) { 69 | var orderBook orderbook.Base 70 | orderbookNew, err := b.GetOrderBook(exchange.FormatExchangeCurrency(b.GetName(), p).String(), 100) 71 | if err != nil { 72 | return orderBook, err 73 | } 74 | 75 | for x := range orderbookNew.Bids { 76 | data := orderbookNew.Bids[x] 77 | orderBook.Bids = append(orderBook.Bids, orderbook.Item{Price: data[0], Amount: data[1]}) 78 | } 79 | 80 | for x := range orderbookNew.Asks { 81 | data := orderbookNew.Asks[x] 82 | orderBook.Asks = append(orderBook.Asks, orderbook.Item{Price: data[0], Amount: data[1]}) 83 | } 84 | 85 | orderbook.ProcessOrderbook(b.GetName(), p, orderBook, assetType) 86 | return orderbook.GetOrderbook(b.Name, p, assetType) 87 | } 88 | 89 | // GetExchangeAccountInfo : Retrieves balances for all enabled currencies for 90 | // the Kraken exchange - TODO 91 | func (b *BTCC) GetExchangeAccountInfo() (exchange.AccountInfo, error) { 92 | var response exchange.AccountInfo 93 | response.ExchangeName = b.GetName() 94 | return response, nil 95 | } 96 | -------------------------------------------------------------------------------- /exchanges/wex/wex_test.go: -------------------------------------------------------------------------------- 1 | package wex 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/thrasher-/gocryptotrader/config" 7 | ) 8 | 9 | var w WEX 10 | 11 | // Please supply your own keys for better unit testing 12 | const ( 13 | apiKey = "" 14 | apiSecret = "" 15 | ) 16 | 17 | func TestSetDefaults(t *testing.T) { 18 | w.SetDefaults() 19 | } 20 | 21 | func TestSetup(t *testing.T) { 22 | wexConfig := config.GetConfig() 23 | wexConfig.LoadConfig("../../testdata/configtest.json") 24 | conf, err := wexConfig.GetExchangeConfig("WEX") 25 | if err != nil { 26 | t.Error("Test Failed - WEX init error") 27 | } 28 | conf.APIKey = apiKey 29 | conf.APISecret = apiSecret 30 | conf.AuthenticatedAPISupport = true 31 | 32 | w.Setup(conf) 33 | } 34 | 35 | func TestGetFee(t *testing.T) { 36 | if w.GetFee() != 0.2 { 37 | t.Error("Test Failed - GetFee() error") 38 | } 39 | } 40 | 41 | func TestGetInfo(t *testing.T) { 42 | _, err := w.GetInfo() 43 | if err != nil { 44 | t.Error("Test Failed - GetInfo() error") 45 | } 46 | } 47 | 48 | func TestGetTicker(t *testing.T) { 49 | _, err := w.GetTicker("btc_usd") 50 | if err != nil { 51 | t.Error("Test Failed - GetTicker() error", err) 52 | } 53 | } 54 | 55 | func TestGetDepth(t *testing.T) { 56 | _, err := w.GetDepth("btc_usd") 57 | if err != nil { 58 | t.Error("Test Failed - GetDepth() error", err) 59 | } 60 | } 61 | 62 | func TestGetTrades(t *testing.T) { 63 | _, err := w.GetTrades("btc_usd") 64 | if err != nil { 65 | t.Error("Test Failed - GetTrades() error", err) 66 | } 67 | } 68 | 69 | func TestGetAccountInfo(t *testing.T) { 70 | _, err := w.GetAccountInfo() 71 | if err == nil { 72 | t.Error("Test Failed - GetAccountInfo() error", err) 73 | } 74 | } 75 | 76 | func TestGetActiveOrders(t *testing.T) { 77 | _, err := w.GetActiveOrders("") 78 | if err == nil { 79 | t.Error("Test Failed - GetActiveOrders() error", err) 80 | } 81 | } 82 | 83 | func TestGetOrderInfo(t *testing.T) { 84 | _, err := w.GetOrderInfo(6196974) 85 | if err == nil { 86 | t.Error("Test Failed - GetOrderInfo() error", err) 87 | } 88 | } 89 | 90 | func TestCancelOrder(t *testing.T) { 91 | _, err := w.CancelOrder(1337) 92 | if err == nil { 93 | t.Error("Test Failed - CancelOrder() error", err) 94 | } 95 | } 96 | 97 | func TestTrade(t *testing.T) { 98 | _, err := w.Trade("", "buy", 0, 0) 99 | if err == nil { 100 | t.Error("Test Failed - Trade() error", err) 101 | } 102 | } 103 | 104 | func TestGetTransactionHistory(t *testing.T) { 105 | _, err := w.GetTransactionHistory(0, 0, 0, "", "", "") 106 | if err == nil { 107 | t.Error("Test Failed - GetTransactionHistory() error", err) 108 | } 109 | } 110 | 111 | func TestGetTradeHistory(t *testing.T) { 112 | _, err := w.GetTradeHistory(0, 0, 0, "", "", "", "") 113 | if err == nil { 114 | t.Error("Test Failed - GetTradeHistory() error", err) 115 | } 116 | } 117 | 118 | func TestWithdrawCoins(t *testing.T) { 119 | _, err := w.WithdrawCoins("", 0, "") 120 | if err == nil { 121 | t.Error("Test Failed - WithdrawCoins() error", err) 122 | } 123 | } 124 | 125 | func TestCoinDepositAddress(t *testing.T) { 126 | _, err := w.CoinDepositAddress("btc") 127 | if err == nil { 128 | t.Error("Test Failed - WithdrawCoins() error", err) 129 | } 130 | } 131 | 132 | func TestCreateCoupon(t *testing.T) { 133 | _, err := w.CreateCoupon("bla", 0) 134 | if err == nil { 135 | t.Error("Test Failed - CreateCoupon() error", err) 136 | } 137 | } 138 | 139 | func TestRedeemCoupon(t *testing.T) { 140 | _, err := w.RedeemCoupon("bla") 141 | if err == nil { 142 | t.Error("Test Failed - RedeemCoupon() error", err) 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /exchanges/localbitcoins/localbitcoins_wrapper.go: -------------------------------------------------------------------------------- 1 | package localbitcoins 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/thrasher-/gocryptotrader/currency/pair" 7 | "github.com/thrasher-/gocryptotrader/exchanges" 8 | "github.com/thrasher-/gocryptotrader/exchanges/orderbook" 9 | "github.com/thrasher-/gocryptotrader/exchanges/ticker" 10 | ) 11 | 12 | // Start starts the LocalBitcoins go routine 13 | func (l *LocalBitcoins) Start() { 14 | go l.Run() 15 | } 16 | 17 | // Run implements the LocalBitcoins wrapper 18 | func (l *LocalBitcoins) Run() { 19 | if l.Verbose { 20 | log.Printf("%s polling delay: %ds.\n", l.GetName(), l.RESTPollingDelay) 21 | log.Printf("%s %d currencies enabled: %s.\n", l.GetName(), len(l.EnabledPairs), l.EnabledPairs) 22 | } 23 | } 24 | 25 | // UpdateTicker updates and returns the ticker for a currency pair 26 | func (l *LocalBitcoins) UpdateTicker(p pair.CurrencyPair, assetType string) (ticker.Price, error) { 27 | var tickerPrice ticker.Price 28 | tick, err := l.GetTicker() 29 | if err != nil { 30 | return tickerPrice, err 31 | } 32 | 33 | for _, x := range l.GetEnabledCurrencies() { 34 | currency := x.SecondCurrency.String() 35 | var tp ticker.Price 36 | tp.Pair = x 37 | tp.Last = tick[currency].Avg24h 38 | tp.Volume = tick[currency].VolumeBTC 39 | ticker.ProcessTicker(l.GetName(), x, tp, assetType) 40 | } 41 | 42 | return ticker.GetTicker(l.GetName(), p, assetType) 43 | } 44 | 45 | // GetTickerPrice returns the ticker for a currency pair 46 | func (l *LocalBitcoins) GetTickerPrice(p pair.CurrencyPair, assetType string) (ticker.Price, error) { 47 | tickerNew, err := ticker.GetTicker(l.GetName(), p, assetType) 48 | if err == nil { 49 | return l.UpdateTicker(p, assetType) 50 | } 51 | return tickerNew, nil 52 | } 53 | 54 | // GetOrderbookEx returns orderbook base on the currency pair 55 | func (l *LocalBitcoins) GetOrderbookEx(p pair.CurrencyPair, assetType string) (orderbook.Base, error) { 56 | ob, err := orderbook.GetOrderbook(l.GetName(), p, assetType) 57 | if err == nil { 58 | return l.UpdateOrderbook(p, assetType) 59 | } 60 | return ob, nil 61 | } 62 | 63 | // UpdateOrderbook updates and returns the orderbook for a currency pair 64 | func (l *LocalBitcoins) UpdateOrderbook(p pair.CurrencyPair, assetType string) (orderbook.Base, error) { 65 | var orderBook orderbook.Base 66 | orderbookNew, err := l.GetOrderbook(p.GetSecondCurrency().String()) 67 | if err != nil { 68 | return orderBook, err 69 | } 70 | 71 | for x := range orderbookNew.Bids { 72 | data := orderbookNew.Bids[x] 73 | orderBook.Bids = append(orderBook.Bids, orderbook.Item{Amount: data.Amount, Price: data.Price}) 74 | } 75 | 76 | for x := range orderbookNew.Asks { 77 | data := orderbookNew.Asks[x] 78 | orderBook.Asks = append(orderBook.Asks, orderbook.Item{Amount: data.Amount, Price: data.Price}) 79 | } 80 | 81 | orderbook.ProcessOrderbook(l.GetName(), p, orderBook, assetType) 82 | return orderbook.GetOrderbook(l.Name, p, assetType) 83 | } 84 | 85 | // GetExchangeAccountInfo retrieves balances for all enabled currencies for the 86 | // LocalBitcoins exchange 87 | func (l *LocalBitcoins) GetExchangeAccountInfo() (exchange.AccountInfo, error) { 88 | var response exchange.AccountInfo 89 | response.ExchangeName = l.GetName() 90 | accountBalance, err := l.GetWalletBalance() 91 | if err != nil { 92 | return response, err 93 | } 94 | var exchangeCurrency exchange.AccountCurrencyInfo 95 | exchangeCurrency.CurrencyName = "BTC" 96 | exchangeCurrency.TotalValue = accountBalance.Total.Balance 97 | 98 | response.Currencies = append(response.Currencies, exchangeCurrency) 99 | return response, nil 100 | } 101 | -------------------------------------------------------------------------------- /exchanges/lakebtc/lakebtc_types.go: -------------------------------------------------------------------------------- 1 | package lakebtc 2 | 3 | // Ticker holds ticker information 4 | type Ticker struct { 5 | Last float64 6 | Bid float64 7 | Ask float64 8 | High float64 9 | Low float64 10 | Volume float64 11 | } 12 | 13 | // OrderbookStructure stores price and amount for order books 14 | type OrderbookStructure struct { 15 | Price float64 16 | Amount float64 17 | } 18 | 19 | // Orderbook contains arrays of orderbook information 20 | type Orderbook struct { 21 | Bids []OrderbookStructure `json:"bids"` 22 | Asks []OrderbookStructure `json:"asks"` 23 | } 24 | 25 | // TickerResponse stores temp response 26 | // Silly hack due to API returning null instead of strings 27 | type TickerResponse struct { 28 | Last interface{} 29 | Bid interface{} 30 | Ask interface{} 31 | High interface{} 32 | Low interface{} 33 | Volume interface{} 34 | } 35 | 36 | // TradeHistory holds trade history data 37 | type TradeHistory struct { 38 | Date int64 `json:"data"` 39 | Price float64 `json:"price,string"` 40 | Amount float64 `json:"amount,string"` 41 | TID int64 `json:"tid"` 42 | } 43 | 44 | // AccountInfo contains account information 45 | type AccountInfo struct { 46 | Balance map[string]string `json:"balance"` 47 | Locked map[string]string `json:"locked"` 48 | Profile struct { 49 | Email string `json:"email"` 50 | UID string `json:"uid"` 51 | BTCDepositAddress string `json:"btc_deposit_addres"` 52 | } `json:"profile"` 53 | } 54 | 55 | // Trade holds trade information 56 | type Trade struct { 57 | ID int64 `json:"id"` 58 | Result string `json:"result"` 59 | } 60 | 61 | // OpenOrders stores full information on your open orders 62 | type OpenOrders struct { 63 | ID int64 `json:"id"` 64 | Amount float64 `json:"amount,string"` 65 | Price float64 `json:"price,string"` 66 | Symbol string `json:"symbol"` 67 | Type string `json:"type"` 68 | At int64 `json:"at"` 69 | } 70 | 71 | // Orders holds current order information 72 | type Orders struct { 73 | ID int64 `json:"id"` 74 | OriginalAmount float64 `json:"original_amount,string"` 75 | Amount float64 `json:"amount,string"` 76 | Price float64 `json:"price,string"` 77 | Symbol string `json:"symbol"` 78 | Type string `json:"type"` 79 | State string `json:"state"` 80 | At int64 `json:"at"` 81 | } 82 | 83 | // AuthenticatedTradeHistory is a store of personalised auth trade history 84 | type AuthenticatedTradeHistory struct { 85 | Type string `json:"type"` 86 | Symbol string `json:"symbol"` 87 | Amount float64 `json:"amount,string"` 88 | Total float64 `json:"total,string"` 89 | At int64 `json:"at"` 90 | } 91 | 92 | // ExternalAccounts holds external account information 93 | type ExternalAccounts struct { 94 | ID int64 `json:"id,string"` 95 | Type string `json:"type"` 96 | Address string `json:"address"` 97 | Alias interface{} `json:"alias"` 98 | Currencies string `json:"currencies"` 99 | State string `json:"state"` 100 | UpdatedAt int64 `json:"updated_at,string"` 101 | } 102 | 103 | // Withdraw holds withdrawal information 104 | type Withdraw struct { 105 | ID int64 `json:"id,string"` 106 | Amount float64 `json:"amount,string"` 107 | Currency string `json:"currency"` 108 | Fee float64 `json:"fee,string"` 109 | State string `json:"state"` 110 | Source string `json:"source"` 111 | ExternalAccountID int64 `json:"external_account_id,string"` 112 | At int64 `json:"at"` 113 | } 114 | -------------------------------------------------------------------------------- /exchanges/itbit/itbit_wrapper.go: -------------------------------------------------------------------------------- 1 | package itbit 2 | 3 | import ( 4 | "log" 5 | "strconv" 6 | 7 | "github.com/thrasher-/gocryptotrader/currency/pair" 8 | "github.com/thrasher-/gocryptotrader/exchanges" 9 | "github.com/thrasher-/gocryptotrader/exchanges/orderbook" 10 | "github.com/thrasher-/gocryptotrader/exchanges/ticker" 11 | ) 12 | 13 | // Start starts the ItBit go routine 14 | func (i *ItBit) Start() { 15 | go i.Run() 16 | } 17 | 18 | // Run implements the ItBit wrapper 19 | func (i *ItBit) Run() { 20 | if i.Verbose { 21 | log.Printf("%s polling delay: %ds.\n", i.GetName(), i.RESTPollingDelay) 22 | log.Printf("%s %d currencies enabled: %s.\n", i.GetName(), len(i.EnabledPairs), i.EnabledPairs) 23 | } 24 | } 25 | 26 | // UpdateTicker updates and returns the ticker for a currency pair 27 | func (i *ItBit) UpdateTicker(p pair.CurrencyPair, assetType string) (ticker.Price, error) { 28 | var tickerPrice ticker.Price 29 | tick, err := i.GetTicker(exchange.FormatExchangeCurrency(i.Name, 30 | p).String()) 31 | if err != nil { 32 | return tickerPrice, err 33 | } 34 | 35 | tickerPrice.Pair = p 36 | tickerPrice.Ask = tick.Ask 37 | tickerPrice.Bid = tick.Bid 38 | tickerPrice.Last = tick.LastPrice 39 | tickerPrice.High = tick.High24h 40 | tickerPrice.Low = tick.Low24h 41 | tickerPrice.Volume = tick.Volume24h 42 | ticker.ProcessTicker(i.GetName(), p, tickerPrice, assetType) 43 | return ticker.GetTicker(i.Name, p, assetType) 44 | } 45 | 46 | // GetTickerPrice returns the ticker for a currency pair 47 | func (i *ItBit) GetTickerPrice(p pair.CurrencyPair, assetType string) (ticker.Price, error) { 48 | tickerNew, err := ticker.GetTicker(i.GetName(), p, assetType) 49 | if err != nil { 50 | return i.UpdateTicker(p, assetType) 51 | } 52 | return tickerNew, nil 53 | } 54 | 55 | // GetOrderbookEx returns orderbook base on the currency pair 56 | func (i *ItBit) GetOrderbookEx(p pair.CurrencyPair, assetType string) (orderbook.Base, error) { 57 | ob, err := orderbook.GetOrderbook(i.GetName(), p, assetType) 58 | if err == nil { 59 | return i.UpdateOrderbook(p, assetType) 60 | } 61 | return ob, nil 62 | } 63 | 64 | // UpdateOrderbook updates and returns the orderbook for a currency pair 65 | func (i *ItBit) UpdateOrderbook(p pair.CurrencyPair, assetType string) (orderbook.Base, error) { 66 | var orderBook orderbook.Base 67 | orderbookNew, err := i.GetOrderbook(exchange.FormatExchangeCurrency(i.Name, 68 | p).String()) 69 | if err != nil { 70 | return orderBook, err 71 | } 72 | 73 | for x := range orderbookNew.Bids { 74 | data := orderbookNew.Bids[x] 75 | price, err := strconv.ParseFloat(data[0], 64) 76 | if err != nil { 77 | log.Println(err) 78 | } 79 | amount, err := strconv.ParseFloat(data[1], 64) 80 | if err != nil { 81 | log.Println(err) 82 | } 83 | orderBook.Bids = append(orderBook.Bids, orderbook.Item{Amount: amount, Price: price}) 84 | } 85 | 86 | for x := range orderbookNew.Asks { 87 | data := orderbookNew.Asks[x] 88 | price, err := strconv.ParseFloat(data[0], 64) 89 | if err != nil { 90 | log.Println(err) 91 | } 92 | amount, err := strconv.ParseFloat(data[1], 64) 93 | if err != nil { 94 | log.Println(err) 95 | } 96 | orderBook.Asks = append(orderBook.Asks, orderbook.Item{Amount: amount, Price: price}) 97 | } 98 | 99 | orderbook.ProcessOrderbook(i.GetName(), p, orderBook, assetType) 100 | return orderbook.GetOrderbook(i.Name, p, assetType) 101 | } 102 | 103 | // GetExchangeAccountInfo retrieves balances for all enabled currencies for the 104 | //ItBit exchange - to-do 105 | func (i *ItBit) GetExchangeAccountInfo() (exchange.AccountInfo, error) { 106 | var response exchange.AccountInfo 107 | response.ExchangeName = i.GetName() 108 | return response, nil 109 | } 110 | -------------------------------------------------------------------------------- /exchanges/anx/anx_wrapper.go: -------------------------------------------------------------------------------- 1 | package anx 2 | 3 | import ( 4 | "log" 5 | "strconv" 6 | 7 | "github.com/thrasher-/gocryptotrader/currency/pair" 8 | "github.com/thrasher-/gocryptotrader/exchanges" 9 | "github.com/thrasher-/gocryptotrader/exchanges/orderbook" 10 | "github.com/thrasher-/gocryptotrader/exchanges/ticker" 11 | ) 12 | 13 | // Start starts the ANX go routine 14 | func (a *ANX) Start() { 15 | go a.Run() 16 | } 17 | 18 | // Run implements the ANX wrapper 19 | func (a *ANX) Run() { 20 | if a.Verbose { 21 | log.Printf("%s polling delay: %ds.\n", a.GetName(), a.RESTPollingDelay) 22 | log.Printf("%s %d currencies enabled: %s.\n", a.GetName(), len(a.EnabledPairs), a.EnabledPairs) 23 | } 24 | } 25 | 26 | // UpdateTicker updates and returns the ticker for a currency pair 27 | func (a *ANX) UpdateTicker(p pair.CurrencyPair, assetType string) (ticker.Price, error) { 28 | var tickerPrice ticker.Price 29 | tick, err := a.GetTicker(exchange.FormatExchangeCurrency(a.GetName(), p).String()) 30 | if err != nil { 31 | return tickerPrice, err 32 | } 33 | 34 | tickerPrice.Pair = p 35 | 36 | if tick.Data.Sell.Value != "" { 37 | tickerPrice.Ask, err = strconv.ParseFloat(tick.Data.Sell.Value, 64) 38 | if err != nil { 39 | return tickerPrice, err 40 | } 41 | } else { 42 | tickerPrice.Ask = 0 43 | } 44 | 45 | if tick.Data.Buy.Value != "" { 46 | tickerPrice.Bid, err = strconv.ParseFloat(tick.Data.Buy.Value, 64) 47 | if err != nil { 48 | return tickerPrice, err 49 | } 50 | } else { 51 | tickerPrice.Bid = 0 52 | } 53 | 54 | if tick.Data.Low.Value != "" { 55 | tickerPrice.Low, err = strconv.ParseFloat(tick.Data.Low.Value, 64) 56 | if err != nil { 57 | return tickerPrice, err 58 | } 59 | } else { 60 | tickerPrice.Low = 0 61 | } 62 | 63 | if tick.Data.Last.Value != "" { 64 | tickerPrice.Last, err = strconv.ParseFloat(tick.Data.Last.Value, 64) 65 | if err != nil { 66 | return tickerPrice, err 67 | } 68 | } else { 69 | tickerPrice.Last = 0 70 | } 71 | 72 | if tick.Data.Vol.Value != "" { 73 | tickerPrice.Volume, err = strconv.ParseFloat(tick.Data.Vol.Value, 64) 74 | if err != nil { 75 | return tickerPrice, err 76 | } 77 | } else { 78 | tickerPrice.Volume = 0 79 | } 80 | 81 | if tick.Data.High.Value != "" { 82 | tickerPrice.High, err = strconv.ParseFloat(tick.Data.High.Value, 64) 83 | if err != nil { 84 | return tickerPrice, err 85 | } 86 | } else { 87 | tickerPrice.High = 0 88 | } 89 | ticker.ProcessTicker(a.GetName(), p, tickerPrice, assetType) 90 | return ticker.GetTicker(a.Name, p, assetType) 91 | } 92 | 93 | // GetTickerPrice returns the ticker for a currency pair 94 | func (a *ANX) GetTickerPrice(p pair.CurrencyPair, assetType string) (ticker.Price, error) { 95 | tickerNew, err := ticker.GetTicker(a.GetName(), p, assetType) 96 | if err != nil { 97 | return a.UpdateTicker(p, assetType) 98 | } 99 | return tickerNew, nil 100 | } 101 | 102 | // GetOrderbookEx returns the orderbook for a currency pair 103 | func (a *ANX) GetOrderbookEx(p pair.CurrencyPair, assetType string) (orderbook.Base, error) { 104 | ob, err := orderbook.GetOrderbook(a.GetName(), p, assetType) 105 | if err == nil { 106 | return a.UpdateOrderbook(p, assetType) 107 | } 108 | return ob, nil 109 | } 110 | 111 | // UpdateOrderbook updates and returns the orderbook for a currency pair 112 | func (a *ANX) UpdateOrderbook(p pair.CurrencyPair, assetType string) (orderbook.Base, error) { 113 | var orderBook orderbook.Base 114 | return orderBook, nil 115 | } 116 | 117 | //GetExchangeAccountInfo : Retrieves balances for all enabled currencies for the ANX exchange 118 | func (a *ANX) GetExchangeAccountInfo() (exchange.AccountInfo, error) { 119 | var response exchange.AccountInfo 120 | response.ExchangeName = a.GetName() 121 | return response, nil 122 | } 123 | -------------------------------------------------------------------------------- /exchanges/itbit/itbit_test.go: -------------------------------------------------------------------------------- 1 | package itbit 2 | 3 | import ( 4 | "net/url" 5 | "testing" 6 | 7 | "github.com/thrasher-/gocryptotrader/config" 8 | ) 9 | 10 | var i ItBit 11 | 12 | // Please provide your own keys to do proper testing 13 | const ( 14 | apiKey = "" 15 | apiSecret = "" 16 | clientID = "" 17 | ) 18 | 19 | func TestSetDefaults(t *testing.T) { 20 | i.SetDefaults() 21 | } 22 | 23 | func TestSetup(t *testing.T) { 24 | cfg := config.GetConfig() 25 | cfg.LoadConfig("../../testdata/configtest.json") 26 | itbitConfig, err := cfg.GetExchangeConfig("ITBIT") 27 | if err != nil { 28 | t.Error("Test Failed - Gemini Setup() init error") 29 | } 30 | 31 | itbitConfig.AuthenticatedAPISupport = true 32 | itbitConfig.APIKey = apiKey 33 | itbitConfig.APISecret = apiSecret 34 | itbitConfig.ClientID = clientID 35 | 36 | i.Setup(itbitConfig) 37 | } 38 | 39 | func TestGetFee(t *testing.T) { 40 | t.Parallel() 41 | if i.GetFee(true) != -0.1 || i.GetFee(false) != 0.5 { 42 | t.Error("Test Failed - GetFee() error") 43 | } 44 | } 45 | 46 | func TestGetTicker(t *testing.T) { 47 | t.Parallel() 48 | _, err := i.GetTicker("XBTUSD") 49 | if err != nil { 50 | t.Error("Test Failed - GetTicker() error", err) 51 | } 52 | } 53 | 54 | func TestGetOrderbook(t *testing.T) { 55 | t.Parallel() 56 | _, err := i.GetOrderbook("XBTSGD") 57 | if err != nil { 58 | t.Error("Test Failed - GetOrderbook() error", err) 59 | } 60 | } 61 | 62 | func TestGetTradeHistory(t *testing.T) { 63 | t.Parallel() 64 | _, err := i.GetTradeHistory("XBTUSD", "0") 65 | if err != nil { 66 | t.Error("Test Failed - GetTradeHistory() error", err) 67 | } 68 | } 69 | 70 | func TestGetWallets(t *testing.T) { 71 | _, err := i.GetWallets(url.Values{}) 72 | if err == nil { 73 | t.Error("Test Failed - GetWallets() error", err) 74 | } 75 | } 76 | 77 | func TestCreateWallet(t *testing.T) { 78 | _, err := i.CreateWallet("test") 79 | if err == nil { 80 | t.Error("Test Failed - CreateWallet() error", err) 81 | } 82 | } 83 | 84 | func TestGetWallet(t *testing.T) { 85 | _, err := i.GetWallet("1337") 86 | if err == nil { 87 | t.Error("Test Failed - GetWallet() error", err) 88 | } 89 | } 90 | 91 | func TestGetWalletBalance(t *testing.T) { 92 | _, err := i.GetWalletBalance("1337", "XRT") 93 | if err == nil { 94 | t.Error("Test Failed - GetWalletBalance() error", err) 95 | } 96 | } 97 | 98 | func TestGetWalletTrades(t *testing.T) { 99 | _, err := i.GetWalletTrades("1337", url.Values{}) 100 | if err == nil { 101 | t.Error("Test Failed - GetWalletTrades() error", err) 102 | } 103 | } 104 | 105 | func TestGetFundingHistory(t *testing.T) { 106 | _, err := i.GetFundingHistory("1337", url.Values{}) 107 | if err == nil { 108 | t.Error("Test Failed - GetFundingHistory() error", err) 109 | } 110 | } 111 | 112 | func TestPlaceOrder(t *testing.T) { 113 | _, err := i.PlaceOrder("1337", "buy", "limit", "USD", 1, 0.2, "banjo", "sauce") 114 | if err == nil { 115 | t.Error("Test Failed - PlaceOrder() error", err) 116 | } 117 | } 118 | 119 | func TestGetOrder(t *testing.T) { 120 | _, err := i.GetOrder("1337", url.Values{}) 121 | if err == nil { 122 | t.Error("Test Failed - GetOrder() error", err) 123 | } 124 | } 125 | 126 | func TestCancelOrder(t *testing.T) { 127 | t.Skip() 128 | err := i.CancelOrder("1337", "1337order") 129 | if err == nil { 130 | t.Error("Test Failed - CancelOrder() error", err) 131 | } 132 | } 133 | 134 | func TestGetDepositAddress(t *testing.T) { 135 | _, err := i.GetDepositAddress("1337", "AUD") 136 | if err == nil { 137 | t.Error("Test Failed - GetDepositAddress() error", err) 138 | } 139 | } 140 | 141 | func TestWalletTransfer(t *testing.T) { 142 | _, err := i.WalletTransfer("1337", "mywallet", "anotherwallet", 200, "USD") 143 | if err == nil { 144 | t.Error("Test Failed - WalletTransfer() error", err) 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /config/config_encryption.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "bytes" 5 | "crypto/aes" 6 | "crypto/cipher" 7 | "crypto/rand" 8 | "errors" 9 | "fmt" 10 | "io" 11 | "log" 12 | "reflect" 13 | 14 | "github.com/thrasher-/gocryptotrader/common" 15 | ) 16 | 17 | const ( 18 | // EncryptConfirmString has a the general confirmation string to allow us to 19 | // see if the file is correctly encrypted 20 | EncryptConfirmString = "THORS-HAMMER" 21 | errAESBlockSize = "The config file data is too small for the AES required block size" 22 | errNotAPointer = "Error: parameter interface is not a pointer" 23 | ) 24 | 25 | // PromptForConfigEncryption asks for encryption key 26 | func (c *Config) PromptForConfigEncryption() bool { 27 | log.Println("Would you like to encrypt your config file (y/n)?") 28 | 29 | input := "" 30 | _, err := fmt.Scanln(&input) 31 | if err != nil { 32 | return false 33 | } 34 | 35 | if !common.YesOrNo(input) { 36 | c.EncryptConfig = configFileEncryptionDisabled 37 | c.SaveConfig("") 38 | return false 39 | } 40 | return true 41 | } 42 | 43 | // PromptForConfigKey asks for configuration key 44 | func PromptForConfigKey() ([]byte, error) { 45 | var cryptoKey []byte 46 | 47 | for len(cryptoKey) != 32 { 48 | log.Println("Enter password (32 characters):") 49 | 50 | _, err := fmt.Scanln(&cryptoKey) 51 | if err != nil { 52 | return nil, err 53 | } 54 | 55 | if len(cryptoKey) > 32 || len(cryptoKey) < 32 { 56 | log.Println("Please re-enter password (32 characters):") 57 | } 58 | } 59 | nonce := make([]byte, 12) 60 | if _, err := io.ReadFull(rand.Reader, nonce); err != nil { 61 | return nil, err 62 | } 63 | 64 | return cryptoKey, nil 65 | } 66 | 67 | // EncryptConfigFile encrypts configuration data that is parsed in with a key 68 | // and returns it as a byte array with an error 69 | func EncryptConfigFile(configData, key []byte) ([]byte, error) { 70 | block, err := aes.NewCipher(key) 71 | if err != nil { 72 | return nil, err 73 | } 74 | 75 | ciphertext := make([]byte, aes.BlockSize+len(configData)) 76 | iv := ciphertext[:aes.BlockSize] 77 | if _, err := io.ReadFull(rand.Reader, iv); err != nil { 78 | return nil, err 79 | } 80 | 81 | stream := cipher.NewCFBEncrypter(block, iv) 82 | stream.XORKeyStream(ciphertext[aes.BlockSize:], configData) 83 | 84 | appendedFile := []byte(EncryptConfirmString) 85 | appendedFile = append(appendedFile, ciphertext...) 86 | return appendedFile, nil 87 | } 88 | 89 | // DecryptConfigFile decrypts configuration data with the supplied key and 90 | // returns the un-encrypted file as a byte array with an error 91 | func DecryptConfigFile(configData, key []byte) ([]byte, error) { 92 | configData = RemoveECS(configData) 93 | blockDecrypt, err := aes.NewCipher(key) 94 | if err != nil { 95 | return nil, err 96 | } 97 | 98 | if len(configData) < aes.BlockSize { 99 | return nil, errors.New(errAESBlockSize) 100 | } 101 | 102 | iv := configData[:aes.BlockSize] 103 | configData = configData[aes.BlockSize:] 104 | 105 | stream := cipher.NewCFBDecrypter(blockDecrypt, iv) 106 | stream.XORKeyStream(configData, configData) 107 | result := configData 108 | return result, nil 109 | } 110 | 111 | // ConfirmConfigJSON confirms JSON in file 112 | func ConfirmConfigJSON(file []byte, result interface{}) error { 113 | if !common.StringContains(reflect.TypeOf(result).String(), "*") { 114 | return errors.New(errNotAPointer) 115 | } 116 | return common.JSONDecode(file, &result) 117 | } 118 | 119 | // ConfirmECS confirms that the encryption confirmation string is found 120 | func ConfirmECS(file []byte) bool { 121 | subslice := []byte(EncryptConfirmString) 122 | return bytes.Contains(file, subslice) 123 | } 124 | 125 | // RemoveECS removes encryption confirmation string 126 | func RemoveECS(file []byte) []byte { 127 | return bytes.Trim(file, EncryptConfirmString) 128 | } 129 | -------------------------------------------------------------------------------- /exchanges/btcmarkets/btcmarkets_test.go: -------------------------------------------------------------------------------- 1 | package btcmarkets 2 | 3 | import ( 4 | "net/url" 5 | "testing" 6 | "time" 7 | 8 | "github.com/thrasher-/gocryptotrader/config" 9 | ) 10 | 11 | var bm BTCMarkets 12 | 13 | // Please supply your own keys here to do better tests 14 | const ( 15 | apiKey = "" 16 | apiSecret = "" 17 | ) 18 | 19 | func TestSetDefaults(t *testing.T) { 20 | bm.SetDefaults() 21 | } 22 | 23 | func TestSetup(t *testing.T) { 24 | t.Parallel() 25 | b := BTCMarkets{} 26 | b.Name = "BTC Markets" 27 | cfg := config.GetConfig() 28 | cfg.LoadConfig("../../testdata/configtest.json") 29 | bConfig, err := cfg.GetExchangeConfig("BTC Markets") 30 | if err != nil { 31 | t.Error("Test Failed - BTC Markets Setup() init error") 32 | } 33 | 34 | b.SetDefaults() 35 | b.Setup(bConfig) 36 | 37 | if !b.IsEnabled() || b.AuthenticatedAPISupport || b.RESTPollingDelay != time.Duration(10) || 38 | b.Verbose || b.Websocket || len(b.BaseCurrencies) < 1 || 39 | len(b.AvailablePairs) < 1 || len(b.EnabledPairs) < 1 { 40 | t.Error("Test Failed - BTC Markets Setup values not set correctly") 41 | } 42 | 43 | bConfig.Enabled = false 44 | b.Setup(bConfig) 45 | 46 | if b.IsEnabled() { 47 | t.Error("Test failed - BTC Markets TestSetup incorrect value") 48 | } 49 | } 50 | 51 | func TestGetFee(t *testing.T) { 52 | t.Parallel() 53 | if fee := bm.GetFee(); fee == 0 { 54 | t.Error("Test failed - GetFee() error") 55 | } 56 | } 57 | 58 | func TestGetTicker(t *testing.T) { 59 | t.Parallel() 60 | _, err := bm.GetTicker("BTC") 61 | if err != nil { 62 | t.Error("Test failed - GetTicker() error", err) 63 | } 64 | } 65 | 66 | func TestGetOrderbook(t *testing.T) { 67 | t.Parallel() 68 | _, err := bm.GetOrderbook("BTC") 69 | if err != nil { 70 | t.Error("Test failed - GetOrderbook() error", err) 71 | } 72 | } 73 | 74 | func TestGetTrades(t *testing.T) { 75 | t.Parallel() 76 | _, err := bm.GetTrades("BTC", nil) 77 | if err != nil { 78 | t.Error("Test failed - GetTrades() error", err) 79 | } 80 | 81 | val := url.Values{} 82 | val.Set("since", "0") 83 | _, err = bm.GetTrades("BTC", val) 84 | if err != nil { 85 | t.Error("Test failed - GetTrades() error", err) 86 | } 87 | } 88 | 89 | func TestNewOrder(t *testing.T) { 90 | t.Parallel() 91 | _, err := bm.NewOrder("AUD", "BTC", 0, 0, "Bid", "limit", "testTest") 92 | if err == nil { 93 | t.Error("Test failed - NewOrder() error", err) 94 | } 95 | } 96 | 97 | func TestCancelOrder(t *testing.T) { 98 | t.Parallel() 99 | _, err := bm.CancelOrder([]int64{1337}) 100 | if err == nil { 101 | t.Error("Test failed - CancelOrder() error", err) 102 | } 103 | } 104 | 105 | func TestGetOrders(t *testing.T) { 106 | t.Parallel() 107 | _, err := bm.GetOrders("AUD", "BTC", 10, 0, false) 108 | if err == nil { 109 | t.Error("Test failed - GetOrders() error", err) 110 | } 111 | _, err = bm.GetOrders("AUD", "BTC", 10, 0, true) 112 | if err == nil { 113 | t.Error("Test failed - GetOrders() error", err) 114 | } 115 | } 116 | 117 | func TestGetOrderDetail(t *testing.T) { 118 | t.Parallel() 119 | _, err := bm.GetOrderDetail([]int64{1337}) 120 | if err == nil { 121 | t.Error("Test failed - GetOrderDetail() error", err) 122 | } 123 | } 124 | 125 | func TestGetAccountBalance(t *testing.T) { 126 | t.Parallel() 127 | _, err := bm.GetAccountBalance() 128 | if err == nil { 129 | t.Error("Test failed - GetAccountBalance() error", err) 130 | } 131 | } 132 | 133 | func TestWithdrawCrypto(t *testing.T) { 134 | t.Parallel() 135 | _, err := bm.WithdrawCrypto(0, "BTC", "LOLOLOL") 136 | if err == nil { 137 | t.Error("Test failed - WithdrawCrypto() error", err) 138 | } 139 | } 140 | 141 | func TestWithdrawAUD(t *testing.T) { 142 | t.Parallel() 143 | _, err := bm.WithdrawAUD("BLA", "1337", "blawest", "1336", "BTC", 10000000) 144 | if err == nil { 145 | t.Error("Test failed - WithdrawAUD() error", err) 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /exchanges/liqui/liqui_types.go: -------------------------------------------------------------------------------- 1 | package liqui 2 | 3 | // Info holds the current pair information as well as server time 4 | type Info struct { 5 | ServerTime int64 `json:"server_time"` 6 | Pairs map[string]PairData `json:"pairs"` 7 | } 8 | 9 | // PairData is a sub-type for Info 10 | type PairData struct { 11 | DecimalPlaces int `json:"decimal_places"` 12 | MinPrice float64 `json:"min_price"` 13 | MaxPrice float64 `json:"max_price"` 14 | MinAmount float64 `json:"min_amount"` 15 | Hidden int `json:"hidden"` 16 | Fee float64 `json:"fee"` 17 | } 18 | 19 | // Ticker contains ticker information 20 | type Ticker struct { 21 | High float64 22 | Low float64 23 | Avg float64 24 | Vol float64 25 | Vol_cur float64 26 | Last float64 27 | Buy float64 28 | Sell float64 29 | Updated int64 30 | } 31 | 32 | // Orderbook references both ask and bid sides 33 | type Orderbook struct { 34 | Asks [][]float64 `json:"asks"` 35 | Bids [][]float64 `json:"bids"` 36 | } 37 | 38 | // Trades contains trade information 39 | type Trades struct { 40 | Type string `json:"type"` 41 | Price float64 `json:"bid"` 42 | Amount float64 `json:"amount"` 43 | TID int64 `json:"tid"` 44 | Timestamp int64 `json:"timestamp"` 45 | } 46 | 47 | // AccountInfo contains full account details information 48 | type AccountInfo struct { 49 | Funds map[string]float64 `json:"funds"` 50 | Rights struct { 51 | Info bool `json:"info"` 52 | Trade bool `json:"trade"` 53 | Withdraw bool `json:"withdraw"` 54 | } `json:"rights"` 55 | ServerTime float64 `json:"server_time"` 56 | TransactionCount int `json:"transaction_count"` 57 | OpenOrders int `json:"open_orders"` 58 | } 59 | 60 | // ActiveOrders holds active order information 61 | type ActiveOrders struct { 62 | Pair string `json:"pair"` 63 | Type string `json:"sell"` 64 | Amount float64 `json:"amount"` 65 | Rate float64 `json:"rate"` 66 | TimestampCreated float64 `json:"time_created"` 67 | Status int `json:"status"` 68 | } 69 | 70 | // OrderInfo holds specific order information 71 | type OrderInfo struct { 72 | Pair string `json:"pair"` 73 | Type string `json:"sell"` 74 | StartAmount float64 `json:"start_amount"` 75 | Amount float64 `json:"amount"` 76 | Rate float64 `json:"rate"` 77 | TimestampCreated float64 `json:"time_created"` 78 | Status int `json:"status"` 79 | } 80 | 81 | // CancelOrder holds cancelled order information 82 | type CancelOrder struct { 83 | OrderID float64 `json:"order_id"` 84 | Funds map[string]float64 `json:"funds"` 85 | } 86 | 87 | // Trade holds trading information 88 | type Trade struct { 89 | Received float64 `json:"received"` 90 | Remains float64 `json:"remains"` 91 | OrderID float64 `json:"order_id"` 92 | Funds map[string]float64 `json:"funds"` 93 | } 94 | 95 | // TradeHistory contains trade history data 96 | type TradeHistory struct { 97 | Pair string `json:"pair"` 98 | Type string `json:"type"` 99 | Amount float64 `json:"amount"` 100 | Rate float64 `json:"rate"` 101 | OrderID float64 `json:"order_id"` 102 | MyOrder int `json:"is_your_order"` 103 | Timestamp float64 `json:"timestamp"` 104 | } 105 | 106 | // Response is a generalized return type 107 | type Response struct { 108 | Return interface{} `json:"return"` 109 | Success int `json:"success"` 110 | Error string `json:"error"` 111 | } 112 | 113 | // WithdrawCoins shows the amount of coins withdrawn from liqui not yet available 114 | type WithdrawCoins struct { 115 | TID int64 `json:"tId"` 116 | AmountSent float64 `json:"amountSent"` 117 | Funds map[string]float64 `json:"funds"` 118 | } 119 | --------------------------------------------------------------------------------