mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-13 15:09:42 +00:00
codebase: Remove web frontend and related services (#2067)
* codebase: Remove web frontend and related services * refactor: Update StartPPROF to accept context and adjust related tests * refactor: Simplify SetIfZero functions and update related tests * config: Clarify DowngradeConfig method documentation regarding permanent removal of deprecated fields * refactor: Rename setIfZeroAndWarn to setDefaultIfZeroWarn for clarity and update related calls * refactor: Update error handling in DataHistoryManager and remove redundant error variable
This commit is contained in:
24
.github/workflows/tests.yml
vendored
24
.github/workflows/tests.yml
vendored
@@ -158,27 +158,3 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
docker run --env SKIP_WRAPPER_CI_TESTS=true --env CI=true --env GCT_DOCKER_CI=true --rm gct-backend-amd64
|
docker run --env SKIP_WRAPPER_CI_TESTS=true --env CI=true --env GCT_DOCKER_CI=true --rm gct-backend-amd64
|
||||||
|
|
||||||
frontend:
|
|
||||||
name: GoCryptoTrader frontend
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Cancel previous workflow runs
|
|
||||||
uses: styfle/cancel-workflow-action@0.12.1
|
|
||||||
with:
|
|
||||||
access_token: ${{ github.token }}
|
|
||||||
|
|
||||||
- uses: actions/checkout@v5
|
|
||||||
|
|
||||||
- name: Use Node.js
|
|
||||||
uses: actions/setup-node@v5
|
|
||||||
with:
|
|
||||||
node-version: '10.8.x'
|
|
||||||
cache: 'npm'
|
|
||||||
cache-dependency-path: web/package-lock.json
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
run: |
|
|
||||||
cd web/
|
|
||||||
npm install
|
|
||||||
npm run lint
|
|
||||||
npm run build
|
|
||||||
|
|||||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -1,8 +1,8 @@
|
|||||||
wrapperconfig.json
|
wrapperconfig.json
|
||||||
config.json
|
config.json
|
||||||
config.dat
|
config.dat
|
||||||
node_modules
|
|
||||||
lib
|
lib
|
||||||
|
.DS_STORE
|
||||||
|
|
||||||
# VS Code
|
# VS Code
|
||||||
.vscode
|
.vscode
|
||||||
@@ -37,10 +37,6 @@ backtester/btcli/btcli
|
|||||||
sqlboiler.toml
|
sqlboiler.toml
|
||||||
sqlboiler.json
|
sqlboiler.json
|
||||||
|
|
||||||
# GCT API Check
|
|
||||||
backup.json
|
|
||||||
.DS_STORE
|
|
||||||
|
|
||||||
# Designated generated files dir
|
# Designated generated files dir
|
||||||
target/
|
target/
|
||||||
|
|
||||||
|
|||||||
@@ -73,7 +73,6 @@ However, we welcome pull requests for any exchange which does not match this cri
|
|||||||
+ Scripting support. See [gctscript](/gctscript/README.md).
|
+ Scripting support. See [gctscript](/gctscript/README.md).
|
||||||
+ Recent and historic trade processing. See [trades](/exchanges/trade/README.md).
|
+ Recent and historic trade processing. See [trades](/exchanges/trade/README.md).
|
||||||
+ Backtesting application. An event-driven backtesting tool to test and iterate trading strategies using historical or custom data. See [backtester](/backtester/README.md).
|
+ Backtesting application. An event-driven backtesting tool to test and iterate trading strategies using historical or custom data. See [backtester](/backtester/README.md).
|
||||||
+ WebGUI (discontinued).
|
|
||||||
+ Exchange HTTP mock testing. See [mock](/exchanges/mock/README.md).
|
+ Exchange HTTP mock testing. See [mock](/exchanges/mock/README.md).
|
||||||
+ Exchange multichain deposits and withdrawals for specific exchanges. See [multichain transfer support](/docs/MULTICHAIN_TRANSFER_SUPPORT.md).
|
+ Exchange multichain deposits and withdrawals for specific exchanges. See [multichain transfer support](/docs/MULTICHAIN_TRANSFER_SUPPORT.md).
|
||||||
|
|
||||||
@@ -134,7 +133,7 @@ go build -tags=sonic_on
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ Creating strategies requires programming skills. [Here](/backtester/eventhandler
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ go run .
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ If any changes were made, ensure that the `rpc.proto` file is formatted correctl
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ Common contains some basic data types which are used throughout.
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -288,7 +288,7 @@ See below for a set of tables and fields, expected values and what they can do
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ The config builder will ask you all the necessary questions required to create a
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ Use the provided config builder under `/backtester/config/configbuilder` or modi
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ This can also be used to implement other means to load data for the backtester t
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ Trade data represents the raw trading data on an exchange. Every buy or sell act
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ See individual exchange implementations [here](/exchanges) and the interface use
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ Additionally, you can view an example under `./testdata/binance_BTCUSDT_24h-trad
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ Database configuration details can be overridden in the `.strat` config file to
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ Its incredibly risky to enable `real-orders`. *Past performance is no guarantee
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ A flow of the application is as follows:
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ A flow of the application is as follows:
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ The GRPC server is responsible for handling requests from the client. All GRPC f
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ A flow of the application is as follows:
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ Below is an overview of how event handlers are used
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ It is used by `backtest.Backtester` and it accepts appending any struct which im
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ The following steps are taken for the `ExecuteOrder` function:
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ Slippage is calculated in two ways in the GoCryptoTrader Backtester
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ The following steps are taken for the `Update` function:
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ The compliance manager is used to store all events at each time interval. When d
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ Every data event will update and calculate holdings value based on the new price
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ See config package [readme](/backtester/config/README.md) to view the risk relat
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ The sizing package ensures that all potential orders raised are within both the
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ If the strategy config setting `DisableUSDTracking` is `false`, then the GoCrypt
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ Each strategy has a unique name and is to be added to the function `getStrategie
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ The strategy base file has basic implementations of the `strategies.Handler` int
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ This strategy does support strategy customisation in the following ways:
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ This strategy does not support customisation
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ This strategy does support strategy customisation in the following ways:
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ This strategy does support strategy customisation in the following ways:
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ Below is an overview of how events are used
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ The event type is an important base for all other events. It allows for consiste
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ SetAmount(float64)
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ The Kline event type is used to store the candle data of an individual data even
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ The Order Event Type is based on `common.EventHandler` and `common.Directioner`
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ The signal event will contain data such as price, the direction as well as the r
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ No. The already existing `CurrencySettings` will populate the funding manager wi
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ This is currently not supported. If this is a feature you would like to have, pl
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ See the following for instructions on installing Golang in WSL: [here](https://a
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ Upon startup, the GoCryptoTrader Backtester will load the strategy and run it fo
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ Upon startup, the GoCryptoTrader Backtester will load the strategy and run it fo
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ Output example:
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<title>{{.Config.Nickname}} Results</title>
|
<title>{{.Config.Nickname}} Results</title>
|
||||||
<!-- Font Awesome -->
|
<!-- Font Awesome -->
|
||||||
<link rel="icon" href="https://raw.githubusercontent.com/thrasher-corp/gocryptotrader/a1a667bab9150e611dc04bad43fa49457171936a/web/src/assets/images/gctlogo-notext.svg" />
|
<link rel="icon" href="https://raw.githubusercontent.com/thrasher-corp/gocryptotrader/master/docs/assets/gctlogo-notext.svg" />
|
||||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css" rel="stylesheet"/>
|
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css" rel="stylesheet"/>
|
||||||
<!-- Google Fonts -->
|
<!-- Google Fonts -->
|
||||||
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" rel="stylesheet"/>
|
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" rel="stylesheet"/>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# GoCryptoTrader dbseed tool
|
# GoCryptoTrader dbseed tool
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/page-logo.png?raw=true" width="350px" height="350px" hspace="70">
|
<img src="/docs/assets/page-logo.png" width="350px" height="350px" hspace="70">
|
||||||
|
|
||||||
|
|
||||||
[](https://github.com/thrasher-corp/gocryptotrader/actions/workflows/tests.yml)
|
[](https://github.com/thrasher-corp/gocryptotrader/actions/workflows/tests.yml)
|
||||||
@@ -103,7 +103,7 @@ btc markets,
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ upper := strings.ToUpper(testString)
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
{{define "engine apiserver" -}}
|
|
||||||
{{template "header" .}}
|
|
||||||
## Current Features for {{.CapitalName}}
|
|
||||||
+ The API server subsystem is a deprecated service used to host a REST or websocket server to interact with some functions of GoCryptoTrader
|
|
||||||
+ This subsystem is no longer maintained and it is highly encouraged to interact with GRPC endpoints directly where possible
|
|
||||||
+ In order to modify the behaviour of the API server subsystem, you can edit the following inside your config file:
|
|
||||||
|
|
||||||
### deprecatedRPC
|
|
||||||
|
|
||||||
| Config | Description | Example |
|
|
||||||
| ------ | ----------- | ------- |
|
|
||||||
| enabled | If enabled will create a REST server which will listen to commands on the listen address | `true` |
|
|
||||||
| listenAddress | If enabled will listen for REST requests on this address and return a JSON response | `localhost:9050` |
|
|
||||||
|
|
||||||
### websocketRPC
|
|
||||||
|
|
||||||
| Config | Description | Example |
|
|
||||||
| ------ | ----------- | ------- |
|
|
||||||
| enabled | If enabled will create a REST server which will listen to commands on the listen address | `true` |
|
|
||||||
| listenAddress | If enabled will listen for requests on this address and return a JSON response | `localhost:9051` |
|
|
||||||
| connectionLimit | Defines how many connections the websocket RPC server can handle simultanesoly | `1` |
|
|
||||||
| maxAuthFailures | For authenticated endpoints, the amount of failed attempts allowed before disconnection | `3` |
|
|
||||||
| allowInsecureOrigin | Allows use of insecure connections | `true` |
|
|
||||||
|
|
||||||
{{template "donations" .}}
|
|
||||||
{{end}}
|
|
||||||
@@ -74,7 +74,6 @@ However, we welcome pull requests for any exchange which does not match this cri
|
|||||||
+ Scripting support. See [gctscript](/gctscript/README.md).
|
+ Scripting support. See [gctscript](/gctscript/README.md).
|
||||||
+ Recent and historic trade processing. See [trades](/exchanges/trade/README.md).
|
+ Recent and historic trade processing. See [trades](/exchanges/trade/README.md).
|
||||||
+ Backtesting application. An event-driven backtesting tool to test and iterate trading strategies using historical or custom data. See [backtester](/backtester/README.md).
|
+ Backtesting application. An event-driven backtesting tool to test and iterate trading strategies using historical or custom data. See [backtester](/backtester/README.md).
|
||||||
+ WebGUI (discontinued).
|
|
||||||
+ Exchange HTTP mock testing. See [mock](/exchanges/mock/README.md).
|
+ Exchange HTTP mock testing. See [mock](/exchanges/mock/README.md).
|
||||||
+ Exchange multichain deposits and withdrawals for specific exchanges. See [multichain transfer support](/docs/MULTICHAIN_TRANSFER_SUPPORT.md).
|
+ Exchange multichain deposits and withdrawals for specific exchanges. See [multichain transfer support](/docs/MULTICHAIN_TRANSFER_SUPPORT.md).
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{{define "donations" -}}
|
{{define "donations" -}}
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# GoCryptoTrader gRPC client
|
# GoCryptoTrader gRPC client
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/page-logo.png?raw=true" width="350px" height="350px" hspace="70">
|
<img src="/docs/assets/page-logo.png" width="350px" height="350px" hspace="70">
|
||||||
|
|
||||||
[](https://github.com/thrasher-corp/gocryptotrader/actions/workflows/tests.yml)
|
[](https://github.com/thrasher-corp/gocryptotrader/actions/workflows/tests.yml)
|
||||||
[](https://github.com/thrasher-corp/gocryptotrader/blob/master/LICENSE)
|
[](https://github.com/thrasher-corp/gocryptotrader/blob/master/LICENSE)
|
||||||
|
|||||||
@@ -1,200 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/sha256"
|
|
||||||
"encoding/hex"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
gws "github.com/gorilla/websocket"
|
|
||||||
"github.com/thrasher-corp/gocryptotrader/common"
|
|
||||||
"github.com/thrasher-corp/gocryptotrader/config"
|
|
||||||
"github.com/thrasher-corp/gocryptotrader/encoding/json"
|
|
||||||
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Vars for the websocket client
|
|
||||||
var (
|
|
||||||
WSConn *gws.Conn
|
|
||||||
)
|
|
||||||
|
|
||||||
// WebsocketEvent is the struct used for websocket events
|
|
||||||
type WebsocketEvent struct {
|
|
||||||
Exchange string `json:"exchange,omitempty"`
|
|
||||||
AssetType string `json:"assetType,omitempty"`
|
|
||||||
Event string
|
|
||||||
Data any
|
|
||||||
}
|
|
||||||
|
|
||||||
// WebsocketAuth is the struct used for a websocket auth request
|
|
||||||
type WebsocketAuth struct {
|
|
||||||
Username string `json:"username"`
|
|
||||||
Password string `json:"password"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// WebsocketEventResponse is the struct used for websocket event responses
|
|
||||||
type WebsocketEventResponse struct {
|
|
||||||
Event string `json:"event"`
|
|
||||||
Data any `json:"data"`
|
|
||||||
Error string `json:"error"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// WebsocketOrderbookTickerRequest is a struct used for ticker and orderbook
|
|
||||||
// requests
|
|
||||||
type WebsocketOrderbookTickerRequest struct {
|
|
||||||
Exchange string `json:"exchangeName"`
|
|
||||||
Currency string `json:"currency"`
|
|
||||||
AssetType asset.Item `json:"assetType"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// SendWebsocketEvent sends a websocket event message
|
|
||||||
func SendWebsocketEvent(event string, reqData any, result *WebsocketEventResponse) error {
|
|
||||||
req := WebsocketEvent{
|
|
||||||
Event: event,
|
|
||||||
}
|
|
||||||
|
|
||||||
if reqData != nil {
|
|
||||||
req.Data = reqData
|
|
||||||
}
|
|
||||||
|
|
||||||
err := WSConn.WriteJSON(req)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = WSConn.ReadJSON(&result)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if result.Error != "" {
|
|
||||||
return errors.New(result.Error)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
cfg := config.GetConfig()
|
|
||||||
err := cfg.LoadConfig(config.File, true)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to load config file: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
listenAddr := cfg.RemoteControl.WebsocketRPC.ListenAddress
|
|
||||||
wsHost := fmt.Sprintf("ws://%s/ws", net.JoinHostPort(common.ExtractHostOrDefault(listenAddr),
|
|
||||||
strconv.Itoa(common.ExtractPortOrDefault(listenAddr))))
|
|
||||||
log.Printf("Connecting to websocket host: %s", wsHost)
|
|
||||||
|
|
||||||
var dialer gws.Dialer
|
|
||||||
var resp *http.Response
|
|
||||||
WSConn, resp, err = dialer.Dial(wsHost, http.Header{})
|
|
||||||
if err != nil {
|
|
||||||
log.Println("Unable to connect to websocket server")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
resp.Body.Close()
|
|
||||||
log.Println("Connected to websocket!")
|
|
||||||
|
|
||||||
log.Println("Authenticating..")
|
|
||||||
shasum := sha256.Sum256([]byte(cfg.RemoteControl.Password))
|
|
||||||
reqData := WebsocketAuth{
|
|
||||||
Username: cfg.RemoteControl.Username,
|
|
||||||
Password: hex.EncodeToString(shasum[:]),
|
|
||||||
}
|
|
||||||
var wsResp WebsocketEventResponse
|
|
||||||
err = SendWebsocketEvent("auth", reqData, &wsResp)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
log.Println("Authenticated successfully")
|
|
||||||
|
|
||||||
log.Println("Getting config..")
|
|
||||||
err = SendWebsocketEvent("GetConfig", nil, &wsResp)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
log.Printf("Fetched config.")
|
|
||||||
|
|
||||||
dataJSON, err := json.Marshal(&wsResp.Data)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var resultCfg config.Config
|
|
||||||
err = json.Unmarshal(dataJSON, &resultCfg)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Println("Saving config..")
|
|
||||||
origBotName := resultCfg.Name
|
|
||||||
resultCfg.Name = "TEST"
|
|
||||||
err = SendWebsocketEvent("SaveConfig", resultCfg, &wsResp)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
log.Println("Saved config!")
|
|
||||||
resultCfg.Name = origBotName
|
|
||||||
err = SendWebsocketEvent("SaveConfig", resultCfg, &wsResp)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
log.Println("Saved config (restored original bot name)!")
|
|
||||||
|
|
||||||
log.Println("Getting account info..")
|
|
||||||
err = SendWebsocketEvent("GetAccountInfo", nil, &wsResp)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
log.Println("Got account info!")
|
|
||||||
|
|
||||||
log.Println("Getting tickers..")
|
|
||||||
err = SendWebsocketEvent("GetTickers", nil, &wsResp)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
log.Println("Got tickers!")
|
|
||||||
|
|
||||||
log.Println("Getting specific ticker..")
|
|
||||||
dataReq := WebsocketOrderbookTickerRequest{
|
|
||||||
Exchange: "Bitfinex",
|
|
||||||
Currency: "BTCUSD",
|
|
||||||
AssetType: asset.Spot,
|
|
||||||
}
|
|
||||||
|
|
||||||
err = SendWebsocketEvent("GetTicker", dataReq, &wsResp)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
log.Println("Got ticker!")
|
|
||||||
|
|
||||||
log.Println("Getting orderbooks..")
|
|
||||||
err = SendWebsocketEvent("GetOrderbooks", nil, &wsResp)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
log.Println("Got orderbooks!")
|
|
||||||
|
|
||||||
log.Println("Getting specific orderbook..")
|
|
||||||
err = SendWebsocketEvent("GetOrderbook", dataReq, &wsResp)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
log.Println("Got orderbook!")
|
|
||||||
|
|
||||||
for {
|
|
||||||
var wsEvent WebsocketEvent
|
|
||||||
err = WSConn.ReadJSON(&wsEvent)
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("Recv'd: %s", wsEvent.Event)
|
|
||||||
}
|
|
||||||
WSConn.Close()
|
|
||||||
}
|
|
||||||
@@ -36,7 +36,7 @@ upper := strings.ToUpper(testString)
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
4
common/cache/README.md
vendored
4
common/cache/README.md
vendored
@@ -1,6 +1,6 @@
|
|||||||
# GoCryptoTrader package cache
|
# GoCryptoTrader package cache
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/page-logo.png?raw=true" width="350px" height="350px" hspace="70">
|
<img src="/docs/assets/page-logo.png" width="350px" height="350px" hspace="70">
|
||||||
|
|
||||||
|
|
||||||
[](https://github.com/thrasher-corp/gocryptotrader/actions/workflows/tests.yml)
|
[](https://github.com/thrasher-corp/gocryptotrader/actions/workflows/tests.yml)
|
||||||
@@ -48,7 +48,7 @@ func main() {
|
|||||||
```
|
```
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -683,3 +683,13 @@ func (c *Counter) IncrementAndGet() int64 {
|
|||||||
}
|
}
|
||||||
return newID
|
return newID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetIfZero sets the value of p to def if p is the zero value for its type and returns true if it was set
|
||||||
|
func SetIfZero[T comparable](p *T, def T) bool {
|
||||||
|
var zero T
|
||||||
|
if *p != zero {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
*p = def
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|||||||
@@ -680,3 +680,15 @@ func TestNilGuard(t *testing.T) {
|
|||||||
err = NilGuard()
|
err = NilGuard()
|
||||||
require.NoError(t, err, "NilGuard with no arguments must not error")
|
require.NoError(t, err, "NilGuard with no arguments must not error")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSetIfZero(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
s := "hello"
|
||||||
|
changed := SetIfZero(&s, "world")
|
||||||
|
assert.False(t, changed, "SetIfZero should not change a non-zero value")
|
||||||
|
assert.Equal(t, "hello", s, "SetIfZero should not change a non-zero value")
|
||||||
|
s = ""
|
||||||
|
changed = SetIfZero(&s, "world")
|
||||||
|
assert.True(t, changed, "SetIfZero should change a zero value")
|
||||||
|
assert.Equal(t, "world", s, "SetIfZero should change a zero value")
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# GoCryptoTrader package Comms
|
# GoCryptoTrader package Comms
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/page-logo.png?raw=true" width="350px" height="350px" hspace="70">
|
<img src="/docs/assets/page-logo.png" width="350px" height="350px" hspace="70">
|
||||||
|
|
||||||
|
|
||||||
[](https://github.com/thrasher-corp/gocryptotrader/actions/workflows/tests.yml)
|
[](https://github.com/thrasher-corp/gocryptotrader/actions/workflows/tests.yml)
|
||||||
@@ -39,7 +39,7 @@ to be exported out to a defined communication medium
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ via Slack:
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ err := s.Connect
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ err := s.Connect
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ via Telegram:
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -263,7 +263,7 @@ servers are configured by the pool array and attempted first to last allowedDiff
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"slices"
|
"slices"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -1580,46 +1579,25 @@ func (c *Config) Save(writerProvider func() (io.Writer, error)) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckRemoteControlConfig checks to see if the old c.Webserver field is used
|
func setDefaultIfZeroWarn[T comparable](scope, name string, p *T, def T) {
|
||||||
// and migrates the existing settings to the new RemoteControl struct
|
if common.SetIfZero(p, def) {
|
||||||
|
log.Warnf(log.ConfigMgr, "%s field %q not set, defaulting to `%v`", scope, name, def)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckRemoteControlConfig checks and sets default values for the remote control config
|
||||||
func (c *Config) CheckRemoteControlConfig() {
|
func (c *Config) CheckRemoteControlConfig() {
|
||||||
m.Lock()
|
m.Lock()
|
||||||
defer m.Unlock()
|
defer m.Unlock()
|
||||||
|
|
||||||
if c.Webserver != nil {
|
setDefaultIfZeroWarn("Remote control", "username", &c.RemoteControl.Username, DefaultGRPCUsername)
|
||||||
port := common.ExtractPortOrDefault(c.Webserver.ListenAddress)
|
setDefaultIfZeroWarn("Remote control", "password", &c.RemoteControl.Password, DefaultGRPCPassword)
|
||||||
host := common.ExtractHostOrDefault(c.Webserver.ListenAddress)
|
setDefaultIfZeroWarn("Remote control gRPC", "listen address", &c.RemoteControl.GRPC.ListenAddress, "localhost:9052")
|
||||||
|
setDefaultIfZeroWarn("Remote control gRPC", "gRPC proxy listen address", &c.RemoteControl.GRPC.GRPCProxyListenAddress, "localhost:9053")
|
||||||
|
|
||||||
c.RemoteControl = RemoteControlConfig{
|
if c.RemoteControl.GRPC.GRPCProxyEnabled && !c.RemoteControl.GRPC.Enabled {
|
||||||
Username: c.Webserver.AdminUsername,
|
log.Warnln(log.ConfigMgr, "gRPC proxy cannot be enabled when gRPC is disabled, disabling gRPC proxy")
|
||||||
Password: c.Webserver.AdminPassword,
|
c.RemoteControl.GRPC.GRPCProxyEnabled = false
|
||||||
|
|
||||||
DeprecatedRPC: DepcrecatedRPCConfig{
|
|
||||||
Enabled: c.Webserver.Enabled,
|
|
||||||
ListenAddress: host + ":" + strconv.Itoa(port),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
port++
|
|
||||||
c.RemoteControl.WebsocketRPC = WebsocketRPCConfig{
|
|
||||||
Enabled: c.Webserver.Enabled,
|
|
||||||
ListenAddress: host + ":" + strconv.Itoa(port),
|
|
||||||
ConnectionLimit: c.Webserver.WebsocketConnectionLimit,
|
|
||||||
MaxAuthFailures: c.Webserver.WebsocketMaxAuthFailures,
|
|
||||||
AllowInsecureOrigin: c.Webserver.WebsocketAllowInsecureOrigin,
|
|
||||||
}
|
|
||||||
|
|
||||||
port++
|
|
||||||
gRPCProxyPort := port + 1
|
|
||||||
c.RemoteControl.GRPC = GRPCConfig{
|
|
||||||
Enabled: c.Webserver.Enabled,
|
|
||||||
ListenAddress: host + ":" + strconv.Itoa(port),
|
|
||||||
GRPCProxyEnabled: c.Webserver.Enabled,
|
|
||||||
GRPCProxyListenAddress: host + ":" + strconv.Itoa(gRPCProxyPort),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Then flush the old webserver settings
|
|
||||||
c.Webserver = nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1688,7 +1666,6 @@ func (c *Config) UpdateConfig(configPath string, newCfg *Config, dryrun bool) er
|
|||||||
c.GlobalHTTPTimeout = newCfg.GlobalHTTPTimeout
|
c.GlobalHTTPTimeout = newCfg.GlobalHTTPTimeout
|
||||||
c.Portfolio = newCfg.Portfolio
|
c.Portfolio = newCfg.Portfolio
|
||||||
c.Communications = newCfg.Communications
|
c.Communications = newCfg.Communications
|
||||||
c.Webserver = newCfg.Webserver
|
|
||||||
c.Exchanges = newCfg.Exchanges
|
c.Exchanges = newCfg.Exchanges
|
||||||
|
|
||||||
if !dryrun {
|
if !dryrun {
|
||||||
|
|||||||
@@ -1564,40 +1564,23 @@ func TestGetFilePath(t *testing.T) {
|
|||||||
|
|
||||||
func TestCheckRemoteControlConfig(t *testing.T) {
|
func TestCheckRemoteControlConfig(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
var c Config
|
var c Config
|
||||||
c.Webserver = &WebserverConfig{
|
c.RemoteControl = RemoteControlConfig{}
|
||||||
Enabled: true,
|
|
||||||
AdminUsername: "satoshi",
|
|
||||||
AdminPassword: "ultrasecurepassword",
|
|
||||||
ListenAddress: ":9050",
|
|
||||||
WebsocketConnectionLimit: 5,
|
|
||||||
WebsocketMaxAuthFailures: 10,
|
|
||||||
WebsocketAllowInsecureOrigin: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
c.CheckRemoteControlConfig()
|
c.CheckRemoteControlConfig()
|
||||||
|
assert.Equal(t, "admin", c.RemoteControl.Username, "Username default should be set correctly")
|
||||||
if c.RemoteControl.Username != "satoshi" ||
|
assert.Equal(t, "Password", c.RemoteControl.Password, "Password default should be set correctly")
|
||||||
c.RemoteControl.Password != "ultrasecurepassword" ||
|
assert.Equal(t, "localhost:9052", c.RemoteControl.GRPC.ListenAddress, "ListenAddress default should be set correctly")
|
||||||
!c.RemoteControl.GRPC.Enabled ||
|
assert.Equal(t, "localhost:9053", c.RemoteControl.GRPC.GRPCProxyListenAddress, "GRPCProxyListenAddress default should be set correctly")
|
||||||
c.RemoteControl.GRPC.ListenAddress != "localhost:9052" ||
|
assert.False(t, c.RemoteControl.GRPC.Enabled, "gRPC default should be set correctly")
|
||||||
!c.RemoteControl.GRPC.GRPCProxyEnabled ||
|
assert.False(t, c.RemoteControl.GRPC.GRPCProxyEnabled, "gRPCProxyEnabled default should be set correctly")
|
||||||
c.RemoteControl.GRPC.GRPCProxyListenAddress != "localhost:9053" ||
|
c.RemoteControl.GRPC.GRPCProxyEnabled = true
|
||||||
!c.RemoteControl.DeprecatedRPC.Enabled ||
|
c.CheckRemoteControlConfig()
|
||||||
c.RemoteControl.DeprecatedRPC.ListenAddress != "localhost:9050" ||
|
assert.False(t, c.RemoteControl.GRPC.GRPCProxyEnabled, "gRPCProxyEnabled should be set to false when gRPC is not enabled")
|
||||||
!c.RemoteControl.WebsocketRPC.Enabled ||
|
c.RemoteControl.GRPC.Enabled = true
|
||||||
c.RemoteControl.WebsocketRPC.ListenAddress != "localhost:9051" ||
|
c.RemoteControl.GRPC.GRPCProxyEnabled = true
|
||||||
!c.RemoteControl.WebsocketRPC.AllowInsecureOrigin ||
|
c.CheckRemoteControlConfig()
|
||||||
c.RemoteControl.WebsocketRPC.ConnectionLimit != 5 ||
|
assert.True(t, c.RemoteControl.GRPC.Enabled, "gRPC should be true")
|
||||||
c.RemoteControl.WebsocketRPC.MaxAuthFailures != 10 {
|
assert.True(t, c.RemoteControl.GRPC.GRPCProxyEnabled, "gRPCProxyEnabled should be true when gRPC is enabled")
|
||||||
t.Error("unexpected results")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now test to ensure the previous settings are flushed
|
|
||||||
if c.Webserver != nil {
|
|
||||||
t.Error("old webserver settings should be nil")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCheckConfig(t *testing.T) {
|
func TestCheckConfig(t *testing.T) {
|
||||||
|
|||||||
@@ -69,6 +69,8 @@ const (
|
|||||||
DefaultUnsetAPIKey = "Key"
|
DefaultUnsetAPIKey = "Key"
|
||||||
DefaultUnsetAPISecret = "Secret"
|
DefaultUnsetAPISecret = "Secret"
|
||||||
DefaultUnsetAccountPlan = "accountPlan"
|
DefaultUnsetAccountPlan = "accountPlan"
|
||||||
|
DefaultGRPCUsername = "admin"
|
||||||
|
DefaultGRPCPassword = "Password"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Public errors exported by this package
|
// Public errors exported by this package
|
||||||
@@ -111,7 +113,6 @@ type Config struct {
|
|||||||
BankAccounts []banking.Account `json:"bankAccounts"`
|
BankAccounts []banking.Account `json:"bankAccounts"`
|
||||||
|
|
||||||
// Deprecated config settings, will be removed at a future date
|
// Deprecated config settings, will be removed at a future date
|
||||||
Webserver *WebserverConfig `json:"webserver,omitempty"`
|
|
||||||
CurrencyPairFormat *currency.PairFormat `json:"currencyPairFormat,omitempty"`
|
CurrencyPairFormat *currency.PairFormat `json:"currencyPairFormat,omitempty"`
|
||||||
FiatDisplayCurrency *currency.Code `json:"fiatDispayCurrency,omitempty"`
|
FiatDisplayCurrency *currency.Code `json:"fiatDispayCurrency,omitempty"`
|
||||||
Cryptocurrencies *currency.Currencies `json:"cryptocurrencies,omitempty"`
|
Cryptocurrencies *currency.Currencies `json:"cryptocurrencies,omitempty"`
|
||||||
@@ -216,8 +217,10 @@ type Exchange struct {
|
|||||||
|
|
||||||
// Profiler defines the profiler configuration to enable pprof
|
// Profiler defines the profiler configuration to enable pprof
|
||||||
type Profiler struct {
|
type Profiler struct {
|
||||||
Enabled bool `json:"enabled"`
|
Enabled bool `json:"enabled"`
|
||||||
MutexProfileFraction int `json:"mutex_profile_fraction"`
|
MutexProfileFraction int `json:"mutex_profile_fraction"`
|
||||||
|
ListenAddress string `json:"listen_address"`
|
||||||
|
BlockProfileRate int `json:"block_profile_rate"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NTPClientConfig defines a network time protocol configuration to allow for
|
// NTPClientConfig defines a network time protocol configuration to allow for
|
||||||
@@ -239,40 +242,11 @@ type GRPCConfig struct {
|
|||||||
TimeInNanoSeconds bool `json:"timeInNanoSeconds"`
|
TimeInNanoSeconds bool `json:"timeInNanoSeconds"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// DepcrecatedRPCConfig stores the deprecatedRPCConfig settings
|
|
||||||
type DepcrecatedRPCConfig struct {
|
|
||||||
Enabled bool `json:"enabled"`
|
|
||||||
ListenAddress string `json:"listenAddress"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// WebsocketRPCConfig stores the websocket config info
|
|
||||||
type WebsocketRPCConfig struct {
|
|
||||||
Enabled bool `json:"enabled"`
|
|
||||||
ListenAddress string `json:"listenAddress"`
|
|
||||||
ConnectionLimit int `json:"connectionLimit"`
|
|
||||||
MaxAuthFailures int `json:"maxAuthFailures"`
|
|
||||||
AllowInsecureOrigin bool `json:"allowInsecureOrigin"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoteControlConfig stores the RPC services config
|
// RemoteControlConfig stores the RPC services config
|
||||||
type RemoteControlConfig struct {
|
type RemoteControlConfig struct {
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
|
GRPC GRPCConfig `json:"gRPC"`
|
||||||
GRPC GRPCConfig `json:"gRPC"`
|
|
||||||
DeprecatedRPC DepcrecatedRPCConfig `json:"deprecatedRPC"`
|
|
||||||
WebsocketRPC WebsocketRPCConfig `json:"websocketRPC"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// WebserverConfig stores the old webserver config
|
|
||||||
type WebserverConfig struct {
|
|
||||||
Enabled bool `json:"enabled"`
|
|
||||||
AdminUsername string `json:"adminUsername"`
|
|
||||||
AdminPassword string `json:"adminPassword"`
|
|
||||||
ListenAddress string `json:"listenAddress"`
|
|
||||||
WebsocketConnectionLimit int `json:"websocketConnectionLimit"`
|
|
||||||
WebsocketMaxAuthFailures int `json:"websocketMaxAuthFailures"`
|
|
||||||
WebsocketAllowInsecureOrigin bool `json:"websocketAllowInsecureOrigin"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Post holds the bot configuration data
|
// Post holds the bot configuration data
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package versions
|
|||||||
import (
|
import (
|
||||||
v0 "github.com/thrasher-corp/gocryptotrader/config/versions/v0"
|
v0 "github.com/thrasher-corp/gocryptotrader/config/versions/v0"
|
||||||
v1 "github.com/thrasher-corp/gocryptotrader/config/versions/v1"
|
v1 "github.com/thrasher-corp/gocryptotrader/config/versions/v1"
|
||||||
|
v10 "github.com/thrasher-corp/gocryptotrader/config/versions/v10"
|
||||||
v2 "github.com/thrasher-corp/gocryptotrader/config/versions/v2"
|
v2 "github.com/thrasher-corp/gocryptotrader/config/versions/v2"
|
||||||
v3 "github.com/thrasher-corp/gocryptotrader/config/versions/v3"
|
v3 "github.com/thrasher-corp/gocryptotrader/config/versions/v3"
|
||||||
v4 "github.com/thrasher-corp/gocryptotrader/config/versions/v4"
|
v4 "github.com/thrasher-corp/gocryptotrader/config/versions/v4"
|
||||||
@@ -24,4 +25,5 @@ func init() {
|
|||||||
Manager.registerVersion(7, &v7.Version{})
|
Manager.registerVersion(7, &v7.Version{})
|
||||||
Manager.registerVersion(8, &v8.Version{})
|
Manager.registerVersion(8, &v8.Version{})
|
||||||
Manager.registerVersion(9, &v9.Version{})
|
Manager.registerVersion(9, &v9.Version{})
|
||||||
|
Manager.registerVersion(10, &v10.Version{})
|
||||||
}
|
}
|
||||||
|
|||||||
22
config/versions/v10/v10.go
Normal file
22
config/versions/v10/v10.go
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package v10
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/buger/jsonparser"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Version implements ConfigVersion
|
||||||
|
type Version struct{}
|
||||||
|
|
||||||
|
// UpgradeConfig checks and removes the deprecatedRPC and websocketRPC fields from the remoteControl config
|
||||||
|
func (*Version) UpgradeConfig(_ context.Context, e []byte) ([]byte, error) {
|
||||||
|
e = jsonparser.Delete(e, "remoteControl", "deprecatedRPC")
|
||||||
|
e = jsonparser.Delete(e, "remoteControl", "websocketRPC")
|
||||||
|
return e, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DowngradeConfig is a no-op. It does not restore deprecatedRPC or websocketRPC on downgrade as their removal is permanent
|
||||||
|
func (*Version) DowngradeConfig(_ context.Context, e []byte) ([]byte, error) {
|
||||||
|
return e, nil
|
||||||
|
}
|
||||||
27
config/versions/v10/v10_test.go
Normal file
27
config/versions/v10/v10_test.go
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package v10_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
v10 "github.com/thrasher-corp/gocryptotrader/config/versions/v10"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestUpgradeConfig(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
in := []byte(`{"remoteControl":{"enabled":true,"deprecatedRPC":{"enabled":true,"listenAddress":"localhost:9050"},"websocketRPC":{"enabled":true,"listenAddress":"localhost:9051","connectionLimit":1,"maxAuthFailures":3,"allowInsecureOrigin":true}}}`)
|
||||||
|
out, err := new(v10.Version).UpgradeConfig(t.Context(), in)
|
||||||
|
require.NoError(t, err)
|
||||||
|
const expected = `{"remoteControl":{"enabled":true}}`
|
||||||
|
assert.JSONEq(t, expected, string(out))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDowngradeConfig(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
in := []byte("meow, moocow, woof, quack")
|
||||||
|
out, err := new(v10.Version).DowngradeConfig(t.Context(), bytes.Clone(in))
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, out, in)
|
||||||
|
}
|
||||||
@@ -72,7 +72,9 @@
|
|||||||
},
|
},
|
||||||
"profiler": {
|
"profiler": {
|
||||||
"enabled": false,
|
"enabled": false,
|
||||||
"mutex_profile_fraction": 0
|
"mutex_profile_fraction": 0,
|
||||||
|
"listen_address": "localhost:8085",
|
||||||
|
"block_profile_rate": 0
|
||||||
},
|
},
|
||||||
"ntpclient": {
|
"ntpclient": {
|
||||||
"enabled": 0,
|
"enabled": 0,
|
||||||
@@ -207,17 +209,6 @@
|
|||||||
"grpcProxyEnabled": false,
|
"grpcProxyEnabled": false,
|
||||||
"grpcProxyListenAddress": "localhost:9053",
|
"grpcProxyListenAddress": "localhost:9053",
|
||||||
"timeInNanoSeconds": false
|
"timeInNanoSeconds": false
|
||||||
},
|
|
||||||
"deprecatedRPC": {
|
|
||||||
"enabled": true,
|
|
||||||
"listenAddress": "localhost:9050"
|
|
||||||
},
|
|
||||||
"websocketRPC": {
|
|
||||||
"enabled": true,
|
|
||||||
"listenAddress": "localhost:9051",
|
|
||||||
"connectionLimit": 1,
|
|
||||||
"maxAuthFailures": 3,
|
|
||||||
"allowInsecureOrigin": true
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"portfolioAddresses": {
|
"portfolioAddresses": {
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ providers.
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ mapstringfloat, err := c.GetRates("USD", "EUR,CHY")
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ mapstringfloat, err := c.GetRates("USD", "EUR,CHY")
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ mapstringfloat, err := c.GetRates("USD", "EUR,CHY")
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ mapstringfloat, err := c.GetRates("USD", "EUR,CHY")
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ mapstringfloat, err := c.GetRates("USD", "EUR,CHY")
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# GoCryptoTrader package Database
|
# GoCryptoTrader package Database
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/page-logo.png?raw=true" width="350px" height="350px" hspace="70">
|
<img src="/docs/assets/page-logo.png" width="350px" height="350px" hspace="70">
|
||||||
|
|
||||||
|
|
||||||
[](https://github.com/thrasher-corp/gocryptotrader/actions/workflows/tests.yml)
|
[](https://github.com/thrasher-corp/gocryptotrader/actions/workflows/tests.yml)
|
||||||
@@ -152,7 +152,7 @@ A helper tool [cmd/dbseed](../cmd/dbseed/README.md) has been created for assisti
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,8 @@
|
|||||||
version: '3'
|
version: '3'
|
||||||
|
|
||||||
services:
|
services:
|
||||||
|
|
||||||
web:
|
|
||||||
build: ./web
|
|
||||||
depends_on:
|
|
||||||
- daemon
|
|
||||||
ports:
|
|
||||||
- "9054:80"
|
|
||||||
|
|
||||||
daemon:
|
daemon:
|
||||||
build: .
|
build: .
|
||||||
ports:
|
ports:
|
||||||
- "9050:9050"
|
|
||||||
- "9051:9051"
|
|
||||||
- "9052:9052"
|
- "9052:9052"
|
||||||
- "9053:9053"
|
- "9053:9053"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# GoCryptoTrader ADD NEW EXCHANGE
|
# GoCryptoTrader ADD NEW EXCHANGE
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/page-logo.png?raw=true" width="350px" height="350px" hspace="70" alt="GoCryptoTrader project logo">
|
<img src="/docs/assets/page-logo.png" width="350px" height="350px" hspace="70" alt="GoCryptoTrader project logo">
|
||||||
|
|
||||||
[](https://github.com/thrasher-corp/gocryptotrader/actions/workflows/tests.yml)
|
[](https://github.com/thrasher-corp/gocryptotrader/actions/workflows/tests.yml)
|
||||||
[](https://github.com/thrasher-corp/gocryptotrader/blob/master/LICENSE)
|
[](https://github.com/thrasher-corp/gocryptotrader/blob/master/LICENSE)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# GoCryptoTrader Unified API
|
# GoCryptoTrader Unified API
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/page-logo.png?raw=true" width="350px" height="350px" hspace="70">
|
<img src="/docs/assets/page-logo.png" width="350px" height="350px" hspace="70">
|
||||||
|
|
||||||
[](https://github.com/thrasher-corp/gocryptotrader/actions/workflows/tests.yml)
|
[](https://github.com/thrasher-corp/gocryptotrader/actions/workflows/tests.yml)
|
||||||
[](https://github.com/thrasher-corp/gocryptotrader/blob/master/LICENSE)
|
[](https://github.com/thrasher-corp/gocryptotrader/blob/master/LICENSE)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# GoCryptoTrader File Hierarchy
|
# GoCryptoTrader File Hierarchy
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/page-logo.png?raw=true" width="350px" height="350px" hspace="70">
|
<img src="/docs/assets/page-logo.png" width="350px" height="350px" hspace="70">
|
||||||
|
|
||||||
[](https://github.com/thrasher-corp/gocryptotrader/actions/workflows/tests.yml)
|
[](https://github.com/thrasher-corp/gocryptotrader/actions/workflows/tests.yml)
|
||||||
[](https://github.com/thrasher-corp/gocryptotrader/blob/master/LICENSE)
|
[](https://github.com/thrasher-corp/gocryptotrader/blob/master/LICENSE)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# GoCryptoTrader OHLCV support
|
# GoCryptoTrader OHLCV support
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/page-logo.png?raw=true" width="350px" height="350px" hspace="70">
|
<img src="/docs/assets/page-logo.png" width="350px" height="350px" hspace="70">
|
||||||
|
|
||||||
[](https://github.com/thrasher-corp/gocryptotrader/actions/workflows/tests.yml)
|
[](https://github.com/thrasher-corp/gocryptotrader/actions/workflows/tests.yml)
|
||||||
[](https://github.com/thrasher-corp/gocryptotrader/blob/master/LICENSE)
|
[](https://github.com/thrasher-corp/gocryptotrader/blob/master/LICENSE)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# GoCryptoTrader Documentation
|
# GoCryptoTrader Documentation
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/page-logo.png?raw=true" width="350px" height="350px" hspace="70">
|
<img src="/docs/assets/page-logo.png" width="350px" height="350px" hspace="70">
|
||||||
|
|
||||||
[](https://github.com/thrasher-corp/gocryptotrader/actions/workflows/tests.yml)
|
[](https://github.com/thrasher-corp/gocryptotrader/actions/workflows/tests.yml)
|
||||||
[](https://github.com/thrasher-corp/gocryptotrader/blob/master/LICENSE)
|
[](https://github.com/thrasher-corp/gocryptotrader/blob/master/LICENSE)
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 9.3 KiB After Width: | Height: | Size: 9.3 KiB |
|
Before Width: | Height: | Size: 9.8 KiB After Width: | Height: | Size: 9.8 KiB |
|
Before Width: | Height: | Size: 73 KiB After Width: | Height: | Size: 73 KiB |
@@ -1,918 +0,0 @@
|
|||||||
package engine
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"crypto/sha256"
|
|
||||||
"encoding/hex"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"net/http/pprof"
|
|
||||||
"runtime"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"sync/atomic"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
|
||||||
gws "github.com/gorilla/websocket"
|
|
||||||
"github.com/thrasher-corp/gocryptotrader/common"
|
|
||||||
"github.com/thrasher-corp/gocryptotrader/config"
|
|
||||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
|
||||||
"github.com/thrasher-corp/gocryptotrader/encoding/json"
|
|
||||||
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
|
||||||
"github.com/thrasher-corp/gocryptotrader/exchanges/orderbook"
|
|
||||||
"github.com/thrasher-corp/gocryptotrader/exchanges/ticker"
|
|
||||||
"github.com/thrasher-corp/gocryptotrader/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
// setupAPIServerManager checks and creates an api server manager
|
|
||||||
func setupAPIServerManager(remoteConfig *config.RemoteControlConfig, pprofConfig *config.Profiler, exchangeManager iExchangeManager, bot iBot, portfolioManager iPortfolioManager, configPath string) (*apiServerManager, error) {
|
|
||||||
if remoteConfig == nil {
|
|
||||||
return nil, errNilRemoteConfig
|
|
||||||
}
|
|
||||||
if pprofConfig == nil {
|
|
||||||
return nil, errNilPProfConfig
|
|
||||||
}
|
|
||||||
if exchangeManager == nil {
|
|
||||||
return nil, errNilExchangeManager
|
|
||||||
}
|
|
||||||
if bot == nil {
|
|
||||||
return nil, errNilBot
|
|
||||||
}
|
|
||||||
if configPath == "" {
|
|
||||||
return nil, errEmptyConfigPath
|
|
||||||
}
|
|
||||||
return &apiServerManager{
|
|
||||||
remoteConfig: remoteConfig,
|
|
||||||
pprofConfig: pprofConfig,
|
|
||||||
restListenAddress: remoteConfig.DeprecatedRPC.ListenAddress,
|
|
||||||
websocketListenAddress: remoteConfig.WebsocketRPC.ListenAddress,
|
|
||||||
exchangeManager: exchangeManager,
|
|
||||||
bot: bot,
|
|
||||||
gctConfigPath: configPath,
|
|
||||||
portfolioManager: portfolioManager,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsRESTServerRunning safely checks whether the subsystem is running
|
|
||||||
func (m *apiServerManager) IsRESTServerRunning() bool {
|
|
||||||
if m == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return atomic.LoadInt32(&m.restStarted) == 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsWebsocketServerRunning safely checks whether the subsystem is running
|
|
||||||
func (m *apiServerManager) IsWebsocketServerRunning() bool {
|
|
||||||
if m == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return atomic.LoadInt32(&m.websocketStarted) == 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// StopRESTServer attempts to shutdown the subsystem
|
|
||||||
func (m *apiServerManager) StopRESTServer() error {
|
|
||||||
if m == nil {
|
|
||||||
return fmt.Errorf("api server %w", ErrNilSubsystem)
|
|
||||||
}
|
|
||||||
if !atomic.CompareAndSwapInt32(&m.restStarted, 1, 0) {
|
|
||||||
return fmt.Errorf("apiserver deprecated server %w", ErrSubSystemNotStarted)
|
|
||||||
}
|
|
||||||
err := m.restHTTPServer.Shutdown(context.Background())
|
|
||||||
if err != nil && !errors.Is(err, http.ErrServerClosed) {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
m.wgRest.Wait()
|
|
||||||
m.restRouter = nil
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *apiServerManager) StopWebsocketServer() error {
|
|
||||||
if m == nil {
|
|
||||||
return fmt.Errorf("api server %w", ErrNilSubsystem)
|
|
||||||
}
|
|
||||||
if !atomic.CompareAndSwapInt32(&m.websocketStarted, 1, 0) {
|
|
||||||
return fmt.Errorf("apiserver websocket server %w", ErrSubSystemNotStarted)
|
|
||||||
}
|
|
||||||
|
|
||||||
err := m.websocketHTTPServer.Shutdown(context.Background())
|
|
||||||
if err != nil && !errors.Is(err, http.ErrServerClosed) {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
m.websocketRouter = nil
|
|
||||||
m.websocketHub = nil
|
|
||||||
m.wgWebsocket.Wait()
|
|
||||||
m.websocketHTTPServer = nil
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// newRouter takes in the exchange interfaces and returns a new multiplexer
|
|
||||||
// router
|
|
||||||
func (m *apiServerManager) newRouter(isREST bool) *mux.Router {
|
|
||||||
router := mux.NewRouter().StrictSlash(true)
|
|
||||||
var routes []Route
|
|
||||||
if common.ExtractPortOrDefault(m.websocketListenAddress) == 80 {
|
|
||||||
m.websocketListenAddress = common.ExtractHostOrDefault(m.websocketListenAddress)
|
|
||||||
} else {
|
|
||||||
m.websocketListenAddress = common.ExtractHostOrDefault(m.websocketListenAddress) + ":" +
|
|
||||||
strconv.Itoa(common.ExtractPortOrDefault(m.websocketListenAddress))
|
|
||||||
}
|
|
||||||
|
|
||||||
if isREST {
|
|
||||||
routes = []Route{
|
|
||||||
{"", http.MethodGet, "/", m.getIndex},
|
|
||||||
{"GetAllSettings", http.MethodGet, "/config/all", m.restGetAllSettings},
|
|
||||||
{"SaveAllSettings", http.MethodPost, "/config/all/save", m.restSaveAllSettings},
|
|
||||||
{"AllEnabledAccountInfo", http.MethodGet, "/exchanges/enabled/accounts/all", m.restGetAllEnabledAccountInfo},
|
|
||||||
{"AllActiveExchangesAndCurrencies", http.MethodGet, "/exchanges/enabled/latest/all", m.restGetAllActiveTickers},
|
|
||||||
{"GetPortfolio", http.MethodGet, "/portfolio/all", m.restGetPortfolio},
|
|
||||||
{"AllActiveExchangesAndOrderbooks", http.MethodGet, "/exchanges/orderbook/latest/all", m.restGetAllActiveOrderbooks},
|
|
||||||
}
|
|
||||||
|
|
||||||
if m.pprofConfig.Enabled {
|
|
||||||
if m.pprofConfig.MutexProfileFraction > 0 {
|
|
||||||
runtime.SetMutexProfileFraction(m.pprofConfig.MutexProfileFraction)
|
|
||||||
}
|
|
||||||
log.Debugf(log.RESTSys,
|
|
||||||
"HTTP Go performance profiler (pprof) endpoint enabled: http://%s:%d/debug/pprof/\n",
|
|
||||||
common.ExtractHostOrDefault(m.websocketListenAddress),
|
|
||||||
common.ExtractPortOrDefault(m.websocketListenAddress),
|
|
||||||
)
|
|
||||||
router.PathPrefix("/debug/pprof/").HandlerFunc(pprof.Index)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
routes = []Route{
|
|
||||||
{"ws", http.MethodGet, "/ws", m.WebsocketClientHandler},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, route := range routes {
|
|
||||||
router.
|
|
||||||
Methods(route.Method).
|
|
||||||
Path(route.Pattern).
|
|
||||||
Name(route.Name).
|
|
||||||
Handler(restLogger(route.HandlerFunc, route.Name)).
|
|
||||||
Host(m.websocketListenAddress)
|
|
||||||
}
|
|
||||||
return router
|
|
||||||
}
|
|
||||||
|
|
||||||
// StartRESTServer starts a REST handler
|
|
||||||
func (m *apiServerManager) StartRESTServer() error {
|
|
||||||
if !atomic.CompareAndSwapInt32(&m.restStarted, 0, 1) {
|
|
||||||
return fmt.Errorf("rest server %w", errAlreadyRunning)
|
|
||||||
}
|
|
||||||
if !m.remoteConfig.DeprecatedRPC.Enabled {
|
|
||||||
atomic.StoreInt32(&m.restStarted, 0)
|
|
||||||
return fmt.Errorf("rest %w", errServerDisabled)
|
|
||||||
}
|
|
||||||
log.Debugf(log.RESTSys,
|
|
||||||
"Deprecated RPC handler support enabled. Listen URL: http://%s:%d\n",
|
|
||||||
common.ExtractHostOrDefault(m.restListenAddress),
|
|
||||||
common.ExtractPortOrDefault(m.restListenAddress),
|
|
||||||
)
|
|
||||||
m.restRouter = m.newRouter(true)
|
|
||||||
if m.restHTTPServer == nil {
|
|
||||||
m.restHTTPServer = &http.Server{
|
|
||||||
Addr: m.restListenAddress,
|
|
||||||
Handler: m.restRouter,
|
|
||||||
ReadHeaderTimeout: time.Minute,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m.wgRest.Go(func() {
|
|
||||||
err := m.restHTTPServer.ListenAndServe()
|
|
||||||
if err != nil {
|
|
||||||
atomic.StoreInt32(&m.restStarted, 0)
|
|
||||||
if !errors.Is(err, http.ErrServerClosed) {
|
|
||||||
log.Errorln(log.APIServerMgr, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// restLogger logs the requests internally
|
|
||||||
func restLogger(inner http.Handler, name string) http.Handler {
|
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
start := time.Now()
|
|
||||||
inner.ServeHTTP(w, r)
|
|
||||||
|
|
||||||
log.Debugf(log.RESTSys,
|
|
||||||
"%s\t%s\t%s\t%s",
|
|
||||||
r.Method,
|
|
||||||
r.RequestURI,
|
|
||||||
name,
|
|
||||||
time.Since(start),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// writeResponse outputs a JSON response of the response interface
|
|
||||||
func writeResponse(w http.ResponseWriter, response any) error {
|
|
||||||
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
return json.NewEncoder(w).Encode(response)
|
|
||||||
}
|
|
||||||
|
|
||||||
// handleError prints the REST method and error
|
|
||||||
func handleError(method string, err error) {
|
|
||||||
log.Errorf(log.APIServerMgr, "RESTful %s: handler failed to send JSON response. Error %s\n",
|
|
||||||
method, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// restGetAllSettings replies to a request with an encoded JSON response about the
|
|
||||||
// trading Bots configuration.
|
|
||||||
func (m *apiServerManager) restGetAllSettings(w http.ResponseWriter, r *http.Request) {
|
|
||||||
err := writeResponse(w, config.GetConfig())
|
|
||||||
if err != nil {
|
|
||||||
handleError(r.Method, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// restSaveAllSettings saves all current settings from request body as a JSON
|
|
||||||
// document then reloads state and returns the settings
|
|
||||||
func (m *apiServerManager) restSaveAllSettings(w http.ResponseWriter, r *http.Request) {
|
|
||||||
// Get the data from the request
|
|
||||||
decoder := json.NewDecoder(r.Body)
|
|
||||||
var responseData config.Post
|
|
||||||
err := decoder.Decode(&responseData)
|
|
||||||
if err != nil {
|
|
||||||
handleError(r.Method, err)
|
|
||||||
}
|
|
||||||
// Save change the settings
|
|
||||||
cfg := config.GetConfig()
|
|
||||||
err = cfg.UpdateConfig(m.gctConfigPath, &responseData.Data, false)
|
|
||||||
if err != nil {
|
|
||||||
handleError(r.Method, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = writeResponse(w, cfg)
|
|
||||||
if err != nil {
|
|
||||||
handleError(r.Method, err)
|
|
||||||
}
|
|
||||||
err = m.bot.SetupExchanges()
|
|
||||||
if err != nil {
|
|
||||||
handleError(r.Method, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// restGetAllActiveOrderbooks returns all enabled exchange orderbooks
|
|
||||||
func (m *apiServerManager) restGetAllActiveOrderbooks(w http.ResponseWriter, r *http.Request) {
|
|
||||||
var response AllEnabledExchangeOrderbooks
|
|
||||||
response.Data = getAllActiveOrderbooks(m.exchangeManager)
|
|
||||||
err := writeResponse(w, response)
|
|
||||||
if err != nil {
|
|
||||||
handleError(r.Method, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// restGetPortfolio returns the Bot portfolio manager
|
|
||||||
func (m *apiServerManager) restGetPortfolio(w http.ResponseWriter, r *http.Request) {
|
|
||||||
result := m.portfolioManager.GetPortfolioSummary()
|
|
||||||
err := writeResponse(w, result)
|
|
||||||
if err != nil {
|
|
||||||
handleError(r.Method, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// restGetAllActiveTickers returns all active tickers
|
|
||||||
func (m *apiServerManager) restGetAllActiveTickers(w http.ResponseWriter, r *http.Request) {
|
|
||||||
var response AllEnabledExchangeCurrencies
|
|
||||||
response.Data = getAllActiveTickers(m.exchangeManager)
|
|
||||||
err := writeResponse(w, response)
|
|
||||||
if err != nil {
|
|
||||||
handleError(r.Method, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// restGetAllEnabledAccountInfo via get request returns JSON response of account
|
|
||||||
// info
|
|
||||||
func (m *apiServerManager) restGetAllEnabledAccountInfo(w http.ResponseWriter, r *http.Request) {
|
|
||||||
response := getAllActiveAccounts(m.exchangeManager)
|
|
||||||
err := writeResponse(w, response)
|
|
||||||
if err != nil {
|
|
||||||
handleError(r.Method, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// getIndex returns an HTML snippet for when a user requests the index URL
|
|
||||||
func (m *apiServerManager) getIndex(w http.ResponseWriter, _ *http.Request) {
|
|
||||||
_, err := fmt.Fprint(w, restIndexResponse)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorln(log.APIServerMgr, err)
|
|
||||||
}
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
}
|
|
||||||
|
|
||||||
// getAllActiveOrderbooks returns all enabled exchanges orderbooks
|
|
||||||
func getAllActiveOrderbooks(m iExchangeManager) []EnabledExchangeOrderbooks {
|
|
||||||
exchanges, err := m.GetExchanges()
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf(log.APIServerMgr, "Cannot get exchanges: %v", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
orderbookData := make([]EnabledExchangeOrderbooks, 0, len(exchanges))
|
|
||||||
for _, e := range exchanges {
|
|
||||||
var orderbooks []orderbook.Book
|
|
||||||
for _, a := range e.GetAssetTypes(true) {
|
|
||||||
pairs, err := e.GetEnabledPairs(a)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf(log.APIServerMgr, "Exchange %s could not retrieve enabled currencies. Err: %s\n", e.GetName(), err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for _, pair := range pairs {
|
|
||||||
ob, err := e.GetCachedOrderbook(pair, a)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf(log.APIServerMgr, "Exchange %s failed to retrieve %s orderbook. Err: %s\n", e.GetName(), pair, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
orderbooks = append(orderbooks, *ob)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
orderbookData = append(orderbookData, EnabledExchangeOrderbooks{ExchangeName: e.GetName(), ExchangeValues: orderbooks})
|
|
||||||
}
|
|
||||||
return orderbookData
|
|
||||||
}
|
|
||||||
|
|
||||||
// getAllActiveTickers returns all enabled exchanges tickers
|
|
||||||
func getAllActiveTickers(m iExchangeManager) []EnabledExchangeCurrencies {
|
|
||||||
exchanges, err := m.GetExchanges()
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf(log.APIServerMgr, "Cannot get exchanges: %v", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
exchangeTickers := make([]EnabledExchangeCurrencies, 0, len(exchanges))
|
|
||||||
for _, e := range exchanges {
|
|
||||||
var tickers []*ticker.Price
|
|
||||||
for _, a := range e.GetAssetTypes(true) {
|
|
||||||
pairs, err := e.GetEnabledPairs(a)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf(log.APIServerMgr, "Exchange %s could not retrieve enabled currencies. Err: %s\n", e.GetName(), err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for _, pair := range pairs {
|
|
||||||
t, err := e.GetCachedTicker(pair, a)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf(log.APIServerMgr, "Exchange %s failed to retrieve %s ticker. Err: %s\n", e.GetName(), pair.String(), err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
tickers = append(tickers, t)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
exchangeTickers = append(exchangeTickers, EnabledExchangeCurrencies{ExchangeName: e.GetName(), ExchangeValues: tickers})
|
|
||||||
}
|
|
||||||
return exchangeTickers
|
|
||||||
}
|
|
||||||
|
|
||||||
// getAllActiveAccounts returns all enabled exchanges accounts
|
|
||||||
func getAllActiveAccounts(m iExchangeManager) []AllEnabledExchangeAccounts {
|
|
||||||
exchanges, err := m.GetExchanges()
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf(log.APIServerMgr, "Cannot get exchanges: %v", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
accounts := make([]AllEnabledExchangeAccounts, 0, len(exchanges))
|
|
||||||
for x := range exchanges {
|
|
||||||
assets := exchanges[x].GetAssetTypes(true)
|
|
||||||
exchName := exchanges[x].GetName()
|
|
||||||
var exchangeAccounts AllEnabledExchangeAccounts
|
|
||||||
for y := range assets {
|
|
||||||
a, err := exchanges[x].GetCachedAccountInfo(context.TODO(), assets[y])
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf(log.APIServerMgr,
|
|
||||||
"Exchange %s failed to retrieve %s ticker. Err: %s\n",
|
|
||||||
exchName,
|
|
||||||
assets[y],
|
|
||||||
err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
exchangeAccounts.Data = append(exchangeAccounts.Data, a)
|
|
||||||
}
|
|
||||||
accounts = append(accounts, exchangeAccounts)
|
|
||||||
}
|
|
||||||
return accounts
|
|
||||||
}
|
|
||||||
|
|
||||||
// StartWebsocketServer starts a Websocket handler
|
|
||||||
func (m *apiServerManager) StartWebsocketServer() error {
|
|
||||||
if !atomic.CompareAndSwapInt32(&m.websocketStarted, 0, 1) {
|
|
||||||
return fmt.Errorf("websocket server %w", errAlreadyRunning)
|
|
||||||
}
|
|
||||||
if !m.remoteConfig.WebsocketRPC.Enabled {
|
|
||||||
atomic.StoreInt32(&m.websocketStarted, 0)
|
|
||||||
return fmt.Errorf("websocket %w", errServerDisabled)
|
|
||||||
}
|
|
||||||
log.Debugf(log.APIServerMgr,
|
|
||||||
"Websocket RPC support enabled. Listen URL: ws://%s:%d/ws\n",
|
|
||||||
common.ExtractHostOrDefault(m.websocketListenAddress),
|
|
||||||
common.ExtractPortOrDefault(m.websocketListenAddress),
|
|
||||||
)
|
|
||||||
m.websocketRouter = m.newRouter(false)
|
|
||||||
if m.websocketHTTPServer == nil {
|
|
||||||
m.websocketHTTPServer = &http.Server{
|
|
||||||
Addr: m.websocketListenAddress,
|
|
||||||
Handler: m.websocketRouter,
|
|
||||||
ReadHeaderTimeout: time.Minute,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m.wgWebsocket.Go(func() {
|
|
||||||
err := m.websocketHTTPServer.ListenAndServe()
|
|
||||||
if err != nil {
|
|
||||||
atomic.StoreInt32(&m.websocketStarted, 0)
|
|
||||||
if !errors.Is(err, http.ErrServerClosed) {
|
|
||||||
log.Errorln(log.APIServerMgr, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// newWebsocketHub Creates a new websocket hub
|
|
||||||
func newWebsocketHub() *websocketHub {
|
|
||||||
return &websocketHub{
|
|
||||||
Broadcast: make(chan []byte),
|
|
||||||
Register: make(chan *websocketClient),
|
|
||||||
Unregister: make(chan *websocketClient),
|
|
||||||
Clients: make(map[*websocketClient]bool),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *websocketHub) run() {
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case client := <-h.Register:
|
|
||||||
h.Clients[client] = true
|
|
||||||
case client := <-h.Unregister:
|
|
||||||
if _, ok := h.Clients[client]; ok {
|
|
||||||
log.Debugln(log.APIServerMgr, "websocket: disconnected client")
|
|
||||||
delete(h.Clients, client)
|
|
||||||
close(client.Send)
|
|
||||||
}
|
|
||||||
case message := <-h.Broadcast:
|
|
||||||
for client := range h.Clients {
|
|
||||||
select {
|
|
||||||
case client.Send <- message:
|
|
||||||
default:
|
|
||||||
log.Debugln(log.APIServerMgr, "websocket: disconnected client")
|
|
||||||
close(client.Send)
|
|
||||||
delete(h.Clients, client)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SendWebsocketMessage sends a websocket event to the client
|
|
||||||
func (c *websocketClient) SendWebsocketMessage(evt any) error {
|
|
||||||
data, err := json.Marshal(evt)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf(log.APIServerMgr, "websocket: failed to send message: %s\n", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
c.Send <- data
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *websocketClient) read() {
|
|
||||||
defer func() {
|
|
||||||
c.Hub.Unregister <- c
|
|
||||||
conErr := c.Conn.Close()
|
|
||||||
if conErr != nil {
|
|
||||||
log.Errorln(log.APIServerMgr, conErr)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
for {
|
|
||||||
msgType, message, err := c.Conn.ReadMessage()
|
|
||||||
if err != nil {
|
|
||||||
if gws.IsUnexpectedCloseError(err, gws.CloseGoingAway, gws.CloseAbnormalClosure) {
|
|
||||||
log.Errorf(log.APIServerMgr, "websocket: client disconnected, err: %s\n", err)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if msgType == gws.TextMessage {
|
|
||||||
var evt WebsocketEvent
|
|
||||||
err := json.Unmarshal(message, &evt)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf(log.APIServerMgr, "websocket: failed to decode JSON sent from client %s\n", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if evt.Event == "" {
|
|
||||||
log.Warnln(log.APIServerMgr, "websocket: client sent a blank event, disconnecting")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
dataJSON, err := json.Marshal(evt.Data)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorln(log.APIServerMgr, "websocket: client sent data we couldn't JSON decode")
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
req := strings.ToLower(evt.Event)
|
|
||||||
log.Debugf(log.APIServerMgr, "websocket: request received: %s\n", req)
|
|
||||||
|
|
||||||
result, ok := wsHandlers[req]
|
|
||||||
if !ok {
|
|
||||||
log.Debugln(log.APIServerMgr, "websocket: unsupported event")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if result.authRequired && !c.Authenticated {
|
|
||||||
log.Warnf(log.APIServerMgr, "Websocket: request %s failed due to unauthenticated request on an authenticated API\n", evt.Event)
|
|
||||||
err = c.SendWebsocketMessage(WebsocketEventResponse{Event: evt.Event, Error: "unauthorised request on authenticated API"})
|
|
||||||
if err != nil {
|
|
||||||
log.Errorln(log.APIServerMgr, err)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
err = result.handler(c, dataJSON)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf(log.APIServerMgr, "websocket: request %s failed. Error %s\n", evt.Event, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *websocketClient) write() {
|
|
||||||
defer func() {
|
|
||||||
err := c.Conn.Close()
|
|
||||||
if err != nil {
|
|
||||||
log.Errorln(log.APIServerMgr, err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
for {
|
|
||||||
message, ok := <-c.Send
|
|
||||||
if !ok {
|
|
||||||
err := c.Conn.WriteMessage(gws.CloseMessage, []byte{})
|
|
||||||
if err != nil {
|
|
||||||
log.Errorln(log.APIServerMgr, err)
|
|
||||||
}
|
|
||||||
log.Debugln(log.APIServerMgr, "websocket: hub closed the channel")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
w, err := c.Conn.NextWriter(gws.TextMessage)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf(log.APIServerMgr, "websocket: failed to create new io.writeCloser: %s\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, err = w.Write(message)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorln(log.APIServerMgr, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add queued chat messages to the current websocket message
|
|
||||||
n := len(c.Send)
|
|
||||||
for range n {
|
|
||||||
_, err = w.Write(<-c.Send)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorln(log.APIServerMgr, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := w.Close(); err != nil {
|
|
||||||
log.Errorf(log.APIServerMgr, "websocket: failed to close io.WriteCloser: %s\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// StartWebsocketHandler starts the websocket hub and routine which
|
|
||||||
// handles clients
|
|
||||||
func StartWebsocketHandler() {
|
|
||||||
if !wsHubStarted {
|
|
||||||
wsHubStarted = true
|
|
||||||
wsHub = newWebsocketHub()
|
|
||||||
go wsHub.run()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// BroadcastWebsocketMessage meow
|
|
||||||
func BroadcastWebsocketMessage(evt WebsocketEvent) error {
|
|
||||||
if !wsHubStarted {
|
|
||||||
return ErrWebsocketServiceNotRunning
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err := json.Marshal(evt)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
wsHub.Broadcast <- data
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// WebsocketClientHandler upgrades the HTTP connection to a websocket
|
|
||||||
// compatible one
|
|
||||||
func (m *apiServerManager) WebsocketClientHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if !wsHubStarted {
|
|
||||||
StartWebsocketHandler()
|
|
||||||
}
|
|
||||||
|
|
||||||
connectionLimit := m.remoteConfig.WebsocketRPC.ConnectionLimit
|
|
||||||
numClients := len(wsHub.Clients)
|
|
||||||
|
|
||||||
if numClients >= connectionLimit {
|
|
||||||
log.Warnf(log.APIServerMgr,
|
|
||||||
"websocket: client rejected due to websocket client limit reached. Number of clients %d. Limit %d.\n",
|
|
||||||
numClients, connectionLimit)
|
|
||||||
w.WriteHeader(http.StatusForbidden)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
upgrader := gws.Upgrader{
|
|
||||||
WriteBufferSize: 1024,
|
|
||||||
ReadBufferSize: 1024,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allow insecure origin if the Origin request header is present and not
|
|
||||||
// equal to the Host request header. Default to false
|
|
||||||
if m.remoteConfig.WebsocketRPC.AllowInsecureOrigin {
|
|
||||||
upgrader.CheckOrigin = func(*http.Request) bool { return true }
|
|
||||||
}
|
|
||||||
|
|
||||||
conn, err := upgrader.Upgrade(w, r, nil)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorln(log.APIServerMgr, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
client := &websocketClient{
|
|
||||||
Hub: wsHub,
|
|
||||||
Conn: conn,
|
|
||||||
Send: make(chan []byte, 1024),
|
|
||||||
maxAuthFailures: m.remoteConfig.WebsocketRPC.MaxAuthFailures,
|
|
||||||
username: m.remoteConfig.Username,
|
|
||||||
password: m.remoteConfig.Password,
|
|
||||||
configPath: m.gctConfigPath,
|
|
||||||
exchangeManager: m.exchangeManager,
|
|
||||||
bot: m.bot,
|
|
||||||
portfolioManager: m.portfolioManager,
|
|
||||||
}
|
|
||||||
|
|
||||||
client.Hub.Register <- client
|
|
||||||
log.Debugf(log.APIServerMgr,
|
|
||||||
"websocket: client connected. Connected clients: %d. Limit %d.\n",
|
|
||||||
numClients+1, connectionLimit)
|
|
||||||
|
|
||||||
go client.read()
|
|
||||||
go client.write()
|
|
||||||
}
|
|
||||||
|
|
||||||
func wsAuth(client *websocketClient, data any) error {
|
|
||||||
d, ok := data.([]byte)
|
|
||||||
if !ok {
|
|
||||||
return common.GetTypeAssertError("[]byte", data)
|
|
||||||
}
|
|
||||||
|
|
||||||
wsResp := WebsocketEventResponse{
|
|
||||||
Event: "auth",
|
|
||||||
}
|
|
||||||
|
|
||||||
var auth WebsocketAuth
|
|
||||||
err := json.Unmarshal(d, &auth)
|
|
||||||
if err != nil {
|
|
||||||
wsResp.Error = err.Error()
|
|
||||||
sendErr := client.SendWebsocketMessage(wsResp)
|
|
||||||
if sendErr != nil {
|
|
||||||
log.Errorln(log.APIServerMgr, sendErr)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
shasum := sha256.Sum256([]byte(client.password))
|
|
||||||
if auth.Username == client.username && auth.Password == hex.EncodeToString(shasum[:]) {
|
|
||||||
client.Authenticated = true
|
|
||||||
wsResp.Data = WebsocketResponseSuccess
|
|
||||||
log.Debugln(log.APIServerMgr,
|
|
||||||
"websocket: client authenticated successfully")
|
|
||||||
return client.SendWebsocketMessage(wsResp)
|
|
||||||
}
|
|
||||||
|
|
||||||
wsResp.Error = "invalid username/password"
|
|
||||||
client.authFailures++
|
|
||||||
sendErr := client.SendWebsocketMessage(wsResp)
|
|
||||||
if sendErr != nil {
|
|
||||||
log.Errorln(log.APIServerMgr, sendErr)
|
|
||||||
}
|
|
||||||
if client.authFailures >= client.maxAuthFailures {
|
|
||||||
log.Debugf(log.APIServerMgr,
|
|
||||||
"websocket: disconnecting client, maximum auth failures threshold reached (failures: %d limit: %d)\n",
|
|
||||||
client.authFailures, client.maxAuthFailures)
|
|
||||||
wsHub.Unregister <- client
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Debugf(log.APIServerMgr,
|
|
||||||
"websocket: client sent wrong username/password (failures: %d limit: %d)\n",
|
|
||||||
client.authFailures, client.maxAuthFailures)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func wsGetConfig(client *websocketClient, _ any) error {
|
|
||||||
wsResp := WebsocketEventResponse{
|
|
||||||
Event: "GetConfig",
|
|
||||||
Data: config.GetConfig(),
|
|
||||||
}
|
|
||||||
return client.SendWebsocketMessage(wsResp)
|
|
||||||
}
|
|
||||||
|
|
||||||
func wsSaveConfig(client *websocketClient, data any) error {
|
|
||||||
d, ok := data.([]byte)
|
|
||||||
if !ok {
|
|
||||||
return common.GetTypeAssertError("[]byte", data)
|
|
||||||
}
|
|
||||||
|
|
||||||
wsResp := WebsocketEventResponse{
|
|
||||||
Event: "SaveConfig",
|
|
||||||
}
|
|
||||||
var respCfg config.Config
|
|
||||||
err := json.Unmarshal(d, &respCfg)
|
|
||||||
if err != nil {
|
|
||||||
wsResp.Error = err.Error()
|
|
||||||
sendErr := client.SendWebsocketMessage(wsResp)
|
|
||||||
if sendErr != nil {
|
|
||||||
log.Errorln(log.APIServerMgr, sendErr)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg := config.GetConfig()
|
|
||||||
err = cfg.UpdateConfig(client.configPath, &respCfg, false)
|
|
||||||
if err != nil {
|
|
||||||
wsResp.Error = err.Error()
|
|
||||||
sendErr := client.SendWebsocketMessage(wsResp)
|
|
||||||
if sendErr != nil {
|
|
||||||
log.Errorln(log.APIServerMgr, sendErr)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = client.bot.SetupExchanges()
|
|
||||||
if err != nil {
|
|
||||||
wsResp.Error = err.Error()
|
|
||||||
sendErr := client.SendWebsocketMessage(wsResp)
|
|
||||||
if sendErr != nil {
|
|
||||||
log.Errorln(log.APIServerMgr, sendErr)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
wsResp.Data = WebsocketResponseSuccess
|
|
||||||
return client.SendWebsocketMessage(wsResp)
|
|
||||||
}
|
|
||||||
|
|
||||||
func wsGetAccountInfo(client *websocketClient, _ any) error {
|
|
||||||
accountInfo := getAllActiveAccounts(client.exchangeManager)
|
|
||||||
wsResp := WebsocketEventResponse{
|
|
||||||
Event: "GetAccountInfo",
|
|
||||||
Data: accountInfo,
|
|
||||||
}
|
|
||||||
return client.SendWebsocketMessage(wsResp)
|
|
||||||
}
|
|
||||||
|
|
||||||
func wsGetTickers(client *websocketClient, _ any) error {
|
|
||||||
wsResp := WebsocketEventResponse{
|
|
||||||
Event: "GetTickers",
|
|
||||||
}
|
|
||||||
wsResp.Data = getAllActiveTickers(client.exchangeManager)
|
|
||||||
return client.SendWebsocketMessage(wsResp)
|
|
||||||
}
|
|
||||||
|
|
||||||
func wsGetTicker(client *websocketClient, data any) error {
|
|
||||||
d, ok := data.([]byte)
|
|
||||||
if !ok {
|
|
||||||
return common.GetTypeAssertError("[]byte", data)
|
|
||||||
}
|
|
||||||
|
|
||||||
wsResp := WebsocketEventResponse{
|
|
||||||
Event: "GetTicker",
|
|
||||||
}
|
|
||||||
var tickerReq WebsocketOrderbookTickerRequest
|
|
||||||
err := json.Unmarshal(d, &tickerReq)
|
|
||||||
if err != nil {
|
|
||||||
wsResp.Error = err.Error()
|
|
||||||
sendErr := client.SendWebsocketMessage(wsResp)
|
|
||||||
if sendErr != nil {
|
|
||||||
log.Errorln(log.APIServerMgr, sendErr)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
p, err := currency.NewPairFromString(tickerReq.Currency)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
a, err := asset.New(tickerReq.AssetType)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
exch, err := client.exchangeManager.GetExchangeByName(tickerReq.Exchange)
|
|
||||||
if err != nil {
|
|
||||||
wsResp.Error = err.Error()
|
|
||||||
sendErr := client.SendWebsocketMessage(wsResp)
|
|
||||||
if sendErr != nil {
|
|
||||||
log.Errorln(log.APIServerMgr, sendErr)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
tick, err := exch.GetCachedTicker(p, a)
|
|
||||||
if err != nil {
|
|
||||||
wsResp.Error = err.Error()
|
|
||||||
sendErr := client.SendWebsocketMessage(wsResp)
|
|
||||||
if sendErr != nil {
|
|
||||||
log.Errorln(log.APIServerMgr, sendErr)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
wsResp.Data = tick
|
|
||||||
return client.SendWebsocketMessage(wsResp)
|
|
||||||
}
|
|
||||||
|
|
||||||
func wsGetOrderbooks(client *websocketClient, _ any) error {
|
|
||||||
wsResp := WebsocketEventResponse{
|
|
||||||
Event: "GetOrderbooks",
|
|
||||||
}
|
|
||||||
wsResp.Data = getAllActiveOrderbooks(client.exchangeManager)
|
|
||||||
return client.SendWebsocketMessage(wsResp)
|
|
||||||
}
|
|
||||||
|
|
||||||
func wsGetOrderbook(client *websocketClient, data any) error {
|
|
||||||
d, ok := data.([]byte)
|
|
||||||
if !ok {
|
|
||||||
return common.GetTypeAssertError("[]byte", data)
|
|
||||||
}
|
|
||||||
|
|
||||||
var orderbookReq WebsocketOrderbookTickerRequest
|
|
||||||
err := json.Unmarshal(d, &orderbookReq)
|
|
||||||
if err != nil {
|
|
||||||
sendErr := client.SendWebsocketMessage(WebsocketEventResponse{Event: "GetOrderbook", Error: err.Error()})
|
|
||||||
if sendErr != nil {
|
|
||||||
log.Errorln(log.APIServerMgr, sendErr)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
p, err := currency.NewPairFromString(orderbookReq.Currency)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
a, err := asset.New(orderbookReq.AssetType)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
exch, err := client.exchangeManager.GetExchangeByName(orderbookReq.Exchange)
|
|
||||||
if err != nil {
|
|
||||||
sendErr := client.SendWebsocketMessage(WebsocketEventResponse{Event: "GetOrderbook", Error: err.Error()})
|
|
||||||
if sendErr != nil {
|
|
||||||
log.Errorln(log.APIServerMgr, sendErr)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
ob, err := exch.GetCachedOrderbook(p, a)
|
|
||||||
if err != nil {
|
|
||||||
sendErr := client.SendWebsocketMessage(WebsocketEventResponse{Event: "GetOrderbook", Error: err.Error()})
|
|
||||||
if sendErr != nil {
|
|
||||||
log.Errorln(log.APIServerMgr, sendErr)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return client.SendWebsocketMessage(WebsocketEventResponse{Event: "GetOrderbook", Data: ob})
|
|
||||||
}
|
|
||||||
|
|
||||||
func wsGetExchangeRates(client *websocketClient, _ any) error {
|
|
||||||
wsResp := WebsocketEventResponse{
|
|
||||||
Event: "GetExchangeRates",
|
|
||||||
}
|
|
||||||
|
|
||||||
var err error
|
|
||||||
wsResp.Data, err = currency.GetExchangeRates()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return client.SendWebsocketMessage(wsResp)
|
|
||||||
}
|
|
||||||
|
|
||||||
func wsGetPortfolio(client *websocketClient, _ any) error {
|
|
||||||
wsResp := WebsocketEventResponse{
|
|
||||||
Event: "GetPortfolio",
|
|
||||||
}
|
|
||||||
|
|
||||||
wsResp.Data = client.portfolioManager.GetPortfolioSummary()
|
|
||||||
return client.SendWebsocketMessage(wsResp)
|
|
||||||
}
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
# GoCryptoTrader package Apiserver
|
|
||||||
|
|
||||||
<img src="/common/gctlogo.png?raw=true" width="350px" height="350px" hspace="70">
|
|
||||||
|
|
||||||
|
|
||||||
[](https://github.com/thrasher-corp/gocryptotrader/actions/workflows/tests.yml)
|
|
||||||
[](https://github.com/thrasher-corp/gocryptotrader/blob/master/LICENSE)
|
|
||||||
[](https://godoc.org/github.com/thrasher-corp/gocryptotrader/engine/apiserver)
|
|
||||||
[](https://codecov.io/gh/thrasher-corp/gocryptotrader)
|
|
||||||
[](https://goreportcard.com/report/github.com/thrasher-corp/gocryptotrader)
|
|
||||||
|
|
||||||
|
|
||||||
This apiserver package is part of the GoCryptoTrader codebase.
|
|
||||||
|
|
||||||
## This is still in active development
|
|
||||||
|
|
||||||
You can track ideas, planned features and what's in progress on our [GoCryptoTrader Kanban board](https://github.com/orgs/thrasher-corp/projects/3).
|
|
||||||
|
|
||||||
Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader Slack](https://join.slack.com/t/gocryptotrader/shared_invite/zt-38z8abs3l-gH8AAOk8XND6DP5NfCiG_g)
|
|
||||||
|
|
||||||
## Current Features for Apiserver
|
|
||||||
+ The API server subsystem is a deprecated service used to host a REST or websocket server to interact with some functions of GoCryptoTrader
|
|
||||||
+ This subsystem is no longer maintained and it is highly encouraged to interact with GRPC endpoints directly where possible
|
|
||||||
+ In order to modify the behaviour of the API server subsystem, you can edit the following inside your config file:
|
|
||||||
|
|
||||||
### deprecatedRPC
|
|
||||||
|
|
||||||
| Config | Description | Example |
|
|
||||||
| ------ | ----------- | ------- |
|
|
||||||
| enabled | If enabled will create a REST server which will listen to commands on the listen address | `true` |
|
|
||||||
| listenAddress | If enabled will listen for REST requests on this address and return a JSON response | `localhost:9050` |
|
|
||||||
|
|
||||||
### websocketRPC
|
|
||||||
|
|
||||||
| Config | Description | Example |
|
|
||||||
| ------ | ----------- | ------- |
|
|
||||||
| enabled | If enabled will create a REST server which will listen to commands on the listen address | `true` |
|
|
||||||
| listenAddress | If enabled will listen for requests on this address and return a JSON response | `localhost:9051` |
|
|
||||||
| connectionLimit | Defines how many connections the websocket RPC server can handle simultanesoly | `1` |
|
|
||||||
| maxAuthFailures | For authenticated endpoints, the amount of failed attempts allowed before disconnection | `3` |
|
|
||||||
| allowInsecureOrigin | Allows use of insecure connections | `true` |
|
|
||||||
|
|
||||||
## Donations
|
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
|
||||||
|
|
||||||
***bc1qk0jareu4jytc0cfrhr5wgshsq8282awpavfahc***
|
|
||||||
@@ -1,224 +0,0 @@
|
|||||||
package engine
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"net/http/httptest"
|
|
||||||
"os"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
"github.com/thrasher-corp/gocryptotrader/config"
|
|
||||||
"github.com/thrasher-corp/gocryptotrader/encoding/json"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestSetupAPIServerManager(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
_, err := setupAPIServerManager(nil, nil, nil, nil, nil, "")
|
|
||||||
assert.ErrorIs(t, err, errNilRemoteConfig)
|
|
||||||
|
|
||||||
_, err = setupAPIServerManager(&config.RemoteControlConfig{}, nil, nil, nil, nil, "")
|
|
||||||
assert.ErrorIs(t, err, errNilPProfConfig)
|
|
||||||
|
|
||||||
_, err = setupAPIServerManager(&config.RemoteControlConfig{}, &config.Profiler{}, nil, nil, nil, "")
|
|
||||||
assert.ErrorIs(t, err, errNilExchangeManager)
|
|
||||||
|
|
||||||
_, err = setupAPIServerManager(&config.RemoteControlConfig{}, &config.Profiler{}, &ExchangeManager{}, nil, nil, "")
|
|
||||||
assert.ErrorIs(t, err, errNilBot)
|
|
||||||
|
|
||||||
_, err = setupAPIServerManager(&config.RemoteControlConfig{}, &config.Profiler{}, &ExchangeManager{}, &fakeBot{}, nil, "")
|
|
||||||
assert.ErrorIs(t, err, errEmptyConfigPath)
|
|
||||||
|
|
||||||
wd, _ := os.Getwd()
|
|
||||||
_, err = setupAPIServerManager(&config.RemoteControlConfig{}, &config.Profiler{}, &ExchangeManager{}, &fakeBot{}, nil, wd)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStartRESTServer(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
wd, _ := os.Getwd()
|
|
||||||
m, err := setupAPIServerManager(&config.RemoteControlConfig{}, &config.Profiler{}, &ExchangeManager{}, &fakeBot{}, nil, wd)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
err = m.StartRESTServer()
|
|
||||||
assert.ErrorIs(t, err, errServerDisabled)
|
|
||||||
|
|
||||||
m.remoteConfig.DeprecatedRPC.Enabled = true
|
|
||||||
err = m.StartRESTServer()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStartWebsocketServer(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
wd, _ := os.Getwd()
|
|
||||||
m, err := setupAPIServerManager(&config.RemoteControlConfig{}, &config.Profiler{}, &ExchangeManager{}, &fakeBot{}, nil, wd)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
err = m.StartWebsocketServer()
|
|
||||||
assert.ErrorIs(t, err, errServerDisabled)
|
|
||||||
|
|
||||||
m.remoteConfig.WebsocketRPC.Enabled = true
|
|
||||||
err = m.StartWebsocketServer()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStopRESTServer(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
wd, _ := os.Getwd()
|
|
||||||
m, err := setupAPIServerManager(&config.RemoteControlConfig{
|
|
||||||
DeprecatedRPC: config.DepcrecatedRPCConfig{
|
|
||||||
Enabled: true,
|
|
||||||
ListenAddress: "localhost:9051",
|
|
||||||
},
|
|
||||||
}, &config.Profiler{}, &ExchangeManager{}, &fakeBot{}, nil, wd)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
err = m.StopRESTServer()
|
|
||||||
assert.ErrorIs(t, err, ErrSubSystemNotStarted)
|
|
||||||
|
|
||||||
err = m.StartRESTServer()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
err = m.StopRESTServer()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
// do it again to ensure things have reset appropriately and no errors occur starting
|
|
||||||
err = m.StartRESTServer()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
err = m.StopRESTServer()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWebsocketStop(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
wd, _ := os.Getwd()
|
|
||||||
m, err := setupAPIServerManager(&config.RemoteControlConfig{
|
|
||||||
WebsocketRPC: config.WebsocketRPCConfig{
|
|
||||||
Enabled: true,
|
|
||||||
ListenAddress: "localhost:9052",
|
|
||||||
},
|
|
||||||
}, &config.Profiler{}, &ExchangeManager{}, &fakeBot{}, nil, wd)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
err = m.StopWebsocketServer()
|
|
||||||
assert.ErrorIs(t, err, ErrSubSystemNotStarted)
|
|
||||||
|
|
||||||
err = m.StartWebsocketServer()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
err = m.StopWebsocketServer()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
// do it again to ensure things have reset appropriately and no errors occur starting
|
|
||||||
err = m.StartWebsocketServer()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
err = m.StopWebsocketServer()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIsRESTServerRunning(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
m := &apiServerManager{}
|
|
||||||
assert.False(t, m.IsRESTServerRunning(), "should return correctly with empty type")
|
|
||||||
m.restStarted = 1
|
|
||||||
assert.True(t, m.IsRESTServerRunning(), "should return correctly with restStarted set")
|
|
||||||
assert.False(t, (*apiServerManager)(nil).IsRESTServerRunning(), "should return correctly on nil type")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIsWebsocketServerRunning(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
m := &apiServerManager{}
|
|
||||||
assert.False(t, m.IsWebsocketServerRunning(), "should return correctly with empty type")
|
|
||||||
m.websocketStarted = 1
|
|
||||||
assert.True(t, m.IsWebsocketServerRunning(), "should return correctly with websocketStarted set")
|
|
||||||
assert.False(t, (*apiServerManager)(nil).IsWebsocketServerRunning(), "should return correctly on nil type")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetAllActiveOrderbooks(t *testing.T) {
|
|
||||||
man := NewExchangeManager()
|
|
||||||
bs, err := man.NewExchangeByName("Bitstamp")
|
|
||||||
require.NoError(t, err, "NewExchangeByName must not error")
|
|
||||||
bs.SetDefaults()
|
|
||||||
err = man.Add(bs)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
resp := getAllActiveOrderbooks(man)
|
|
||||||
assert.NotNil(t, resp)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetAllActiveTickers(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
man := NewExchangeManager()
|
|
||||||
bs, err := man.NewExchangeByName("Bitstamp")
|
|
||||||
require.NoError(t, err, "NewExchangeByName must not error")
|
|
||||||
bs.SetDefaults()
|
|
||||||
err = man.Add(bs)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
resp := getAllActiveTickers(man)
|
|
||||||
assert.NotNil(t, resp)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetAllActiveAccounts(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
man := NewExchangeManager()
|
|
||||||
bs, err := man.NewExchangeByName("Bitstamp")
|
|
||||||
require.NoError(t, err, "NewExchangeByName must not error")
|
|
||||||
bs.SetDefaults()
|
|
||||||
err = man.Add(bs)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
resp := getAllActiveAccounts(man)
|
|
||||||
assert.NotNil(t, resp)
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeHTTPGetRequest(t *testing.T, response any) *http.Response {
|
|
||||||
t.Helper()
|
|
||||||
w := httptest.NewRecorder()
|
|
||||||
|
|
||||||
err := writeResponse(w, response)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
return w.Result()
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestConfigAllJsonResponse test if config/all restful json response is valid
|
|
||||||
func TestConfigAllJsonResponse(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
var c config.Config
|
|
||||||
err := c.LoadConfig(config.TestFile, true)
|
|
||||||
assert.NoError(t, err, "LoadConfig should not error")
|
|
||||||
|
|
||||||
resp := makeHTTPGetRequest(t, c)
|
|
||||||
body, err := io.ReadAll(resp.Body)
|
|
||||||
assert.NoError(t, err, "ReadAll should not error")
|
|
||||||
err = resp.Body.Close()
|
|
||||||
assert.NoError(t, err, "Close body should not error")
|
|
||||||
|
|
||||||
var responseConfig config.Config
|
|
||||||
err = json.Unmarshal(body, &responseConfig)
|
|
||||||
assert.NoError(t, err, "Unmarshal should not error")
|
|
||||||
for i, e := range responseConfig.Exchanges {
|
|
||||||
err = e.CurrencyPairs.SetDelimitersFromConfig()
|
|
||||||
assert.NoError(t, err, "SetDelimitersFromConfig should not error")
|
|
||||||
// Using require here makes it much easier to isolate differences per-exchange than below
|
|
||||||
// We look into pointers separately
|
|
||||||
for a, p := range e.CurrencyPairs.Pairs {
|
|
||||||
require.Equalf(t, c.Exchanges[i].CurrencyPairs.Pairs[a], p, "%s exchange Config CurrencyManager Pairs for asset %s must match api response", e.Name, a)
|
|
||||||
}
|
|
||||||
require.Equalf(t, c.Exchanges[i].CurrencyPairs, e.CurrencyPairs, "%s exchange Config CurrencyManager must match api response", e.Name)
|
|
||||||
require.Equalf(t, c.Exchanges[i], e, "%s exchange Config must match api response", e.Name) // require here makes it much easier to isolate differences than below
|
|
||||||
}
|
|
||||||
assert.Equal(t, c, responseConfig, "Config should match api response")
|
|
||||||
}
|
|
||||||
|
|
||||||
// fakeBot is a basic implementation of the iBot interface used for testing
|
|
||||||
type fakeBot struct{}
|
|
||||||
|
|
||||||
// SetupExchanges is a basic implementation of the iBot interface used for testing
|
|
||||||
func (f *fakeBot) SetupExchanges() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -1,168 +0,0 @@
|
|||||||
package engine
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"net/http"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
|
||||||
gws "github.com/gorilla/websocket"
|
|
||||||
"github.com/thrasher-corp/gocryptotrader/config"
|
|
||||||
"github.com/thrasher-corp/gocryptotrader/exchanges/account"
|
|
||||||
"github.com/thrasher-corp/gocryptotrader/exchanges/orderbook"
|
|
||||||
"github.com/thrasher-corp/gocryptotrader/exchanges/ticker"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Const vars for websocket
|
|
||||||
const (
|
|
||||||
WebsocketResponseSuccess = "OK"
|
|
||||||
restIndexResponse = "<html>GoCryptoTrader RESTful interface. For the web GUI, please visit the <a href=https://github.com/thrasher-corp/gocryptotrader/blob/master/web/README.md>web GUI readme.</a></html>"
|
|
||||||
DeprecatedName = "deprecated_rpc"
|
|
||||||
WebsocketName = "websocket_rpc"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
wsHub *websocketHub
|
|
||||||
wsHubStarted bool
|
|
||||||
errNilRemoteConfig = errors.New("received nil remote config")
|
|
||||||
errNilPProfConfig = errors.New("received nil pprof config")
|
|
||||||
errNilBot = errors.New("received nil engine bot")
|
|
||||||
errEmptyConfigPath = errors.New("received empty config path")
|
|
||||||
errServerDisabled = errors.New("server disabled")
|
|
||||||
errAlreadyRunning = errors.New("already running")
|
|
||||||
// ErrWebsocketServiceNotRunning occurs when a message is sent to be broadcast via websocket
|
|
||||||
// and its not running
|
|
||||||
ErrWebsocketServiceNotRunning = errors.New("websocket service not started")
|
|
||||||
)
|
|
||||||
|
|
||||||
// apiServerManager holds all relevant fields to manage both REST and websocket
|
|
||||||
// api servers
|
|
||||||
type apiServerManager struct {
|
|
||||||
restStarted int32
|
|
||||||
websocketStarted int32
|
|
||||||
restListenAddress string
|
|
||||||
websocketListenAddress string
|
|
||||||
gctConfigPath string
|
|
||||||
restHTTPServer *http.Server
|
|
||||||
websocketHTTPServer *http.Server
|
|
||||||
wgRest sync.WaitGroup
|
|
||||||
wgWebsocket sync.WaitGroup
|
|
||||||
|
|
||||||
restRouter *mux.Router
|
|
||||||
websocketRouter *mux.Router
|
|
||||||
websocketHub *websocketHub
|
|
||||||
|
|
||||||
remoteConfig *config.RemoteControlConfig
|
|
||||||
pprofConfig *config.Profiler
|
|
||||||
exchangeManager iExchangeManager
|
|
||||||
bot iBot
|
|
||||||
portfolioManager iPortfolioManager
|
|
||||||
}
|
|
||||||
|
|
||||||
// websocketClient stores information related to the websocket client
|
|
||||||
type websocketClient struct {
|
|
||||||
Hub *websocketHub
|
|
||||||
Conn *gws.Conn
|
|
||||||
Authenticated bool
|
|
||||||
authFailures int
|
|
||||||
Send chan []byte
|
|
||||||
username string
|
|
||||||
password string
|
|
||||||
maxAuthFailures int
|
|
||||||
exchangeManager iExchangeManager
|
|
||||||
bot iBot
|
|
||||||
portfolioManager iPortfolioManager
|
|
||||||
configPath string
|
|
||||||
}
|
|
||||||
|
|
||||||
// websocketHub stores the data for managing websocket clients
|
|
||||||
type websocketHub struct {
|
|
||||||
Clients map[*websocketClient]bool
|
|
||||||
Broadcast chan []byte
|
|
||||||
Register chan *websocketClient
|
|
||||||
Unregister chan *websocketClient
|
|
||||||
}
|
|
||||||
|
|
||||||
// WebsocketEvent is the struct used for websocket events
|
|
||||||
type WebsocketEvent struct {
|
|
||||||
Exchange string `json:"exchange,omitempty"`
|
|
||||||
AssetType string `json:"assetType,omitempty"`
|
|
||||||
Event string
|
|
||||||
Data any
|
|
||||||
}
|
|
||||||
|
|
||||||
// WebsocketEventResponse is the struct used for websocket event responses
|
|
||||||
type WebsocketEventResponse struct {
|
|
||||||
Event string `json:"event"`
|
|
||||||
Data any `json:"data"`
|
|
||||||
Error string `json:"error"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// WebsocketOrderbookTickerRequest is a struct used for ticker and orderbook
|
|
||||||
// requests
|
|
||||||
type WebsocketOrderbookTickerRequest struct {
|
|
||||||
Exchange string `json:"exchangeName"`
|
|
||||||
Currency string `json:"currency"`
|
|
||||||
AssetType string `json:"assetType"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// WebsocketAuth is a struct used for
|
|
||||||
type WebsocketAuth struct {
|
|
||||||
Username string `json:"username"`
|
|
||||||
Password string `json:"password"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Route is a sub type that holds the request routes
|
|
||||||
type Route struct {
|
|
||||||
Name string
|
|
||||||
Method string
|
|
||||||
Pattern string
|
|
||||||
HandlerFunc http.HandlerFunc
|
|
||||||
}
|
|
||||||
|
|
||||||
// AllEnabledExchangeOrderbooks holds the enabled exchange orderbooks
|
|
||||||
type AllEnabledExchangeOrderbooks struct {
|
|
||||||
Data []EnabledExchangeOrderbooks `json:"data"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// EnabledExchangeOrderbooks is a sub type for singular exchanges and respective
|
|
||||||
// orderbooks
|
|
||||||
type EnabledExchangeOrderbooks struct {
|
|
||||||
ExchangeName string `json:"exchangeName"`
|
|
||||||
ExchangeValues []orderbook.Book `json:"exchangeValues"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// AllEnabledExchangeCurrencies holds the enabled exchange currencies
|
|
||||||
type AllEnabledExchangeCurrencies struct {
|
|
||||||
Data []EnabledExchangeCurrencies `json:"data"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// EnabledExchangeCurrencies is a sub type for singular exchanges and respective
|
|
||||||
// currencies
|
|
||||||
type EnabledExchangeCurrencies struct {
|
|
||||||
ExchangeName string `json:"exchangeName"`
|
|
||||||
ExchangeValues []*ticker.Price `json:"exchangeValues"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// AllEnabledExchangeAccounts holds all enabled accounts info
|
|
||||||
type AllEnabledExchangeAccounts struct {
|
|
||||||
Data []account.Holdings `json:"data"`
|
|
||||||
}
|
|
||||||
|
|
||||||
var wsHandlers = map[string]wsCommandHandler{
|
|
||||||
"auth": {authRequired: false, handler: wsAuth},
|
|
||||||
"getconfig": {authRequired: true, handler: wsGetConfig},
|
|
||||||
"saveconfig": {authRequired: true, handler: wsSaveConfig},
|
|
||||||
"getaccountinfo": {authRequired: true, handler: wsGetAccountInfo},
|
|
||||||
"gettickers": {authRequired: false, handler: wsGetTickers},
|
|
||||||
"getticker": {authRequired: false, handler: wsGetTicker},
|
|
||||||
"getorderbooks": {authRequired: false, handler: wsGetOrderbooks},
|
|
||||||
"getorderbook": {authRequired: false, handler: wsGetOrderbook},
|
|
||||||
"getexchangerates": {authRequired: false, handler: wsGetExchangeRates},
|
|
||||||
"getportfolio": {authRequired: true, handler: wsGetPortfolio},
|
|
||||||
}
|
|
||||||
|
|
||||||
type wsCommandHandler struct {
|
|
||||||
authRequired bool
|
|
||||||
handler func(client *websocketClient, data any) error
|
|
||||||
}
|
|
||||||
@@ -68,7 +68,7 @@ Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ strategies.
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader
|
|||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
<img src="/docs/assets/donate.png" hspace="70">
|
||||||
|
|
||||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user