Skip to content

Commit 82f7e49

Browse files
committed
Support for binance testnet
1 parent 27fdfa2 commit 82f7e49

11 files changed

+79
-40
lines changed

Bot/Exchange/Binance/BinanceWebsocket.py

+9-3
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,14 @@
2020
from retrying import retry
2121
from websockets.connection import State
2222

23+
import Utils.Utils
2324
from Utils.Logger import Logger
2425

2526

2627
class BinanceWebsocket(Thread, Logger):
2728
REFRESH_KEY_TIMEOUT = 30 * 60
2829
WS_URL = 'wss://stream.binance.com:9443/'
30+
WS_TEST_URL = 'wss://testnet.binance.vision/'
2931
__EVENT_LOOP = None
3032

3133
def __init__(self, client: Client):
@@ -60,6 +62,10 @@ def __init__(self, client: Client):
6062
self.name = 'Binance WebSocket Thread'
6163

6264

65+
def get_url(self):
66+
return BinanceWebsocket.WS_TEST_URL if Utils.Utils.is_simulation() else BinanceWebsocket.WS_URL
67+
68+
6369
@retry(
6470
stop_max_attempt_number=3,
6571
wait_fixed=1000
@@ -116,10 +122,10 @@ def start_ticker(self, symbols=None, callback=None):
116122
self.ticker_cb = callback
117123

118124
if symbols:
119-
url = os.path.join(BinanceWebsocket.WS_URL,
125+
url = os.path.join(self.get_url(),
120126
'stream?streams=' + '/'.join([s.lower() + '@ticker' for s in symbols]))
121127
else:
122-
url = os.path.join(BinanceWebsocket.WS_URL, 'ws/!ticker@arr')
128+
url = os.path.join(self.get_url(), 'ws/!ticker@arr')
123129

124130
self.ticker_ws_future = asyncio.run_coroutine_threadsafe(self.websocket_handler(url, self.ticker_cb, True), self.loop)
125131

@@ -167,7 +173,7 @@ def listen_key_received(self, future: asyncio.Future):
167173
if create_user_ws:
168174
self.stop_user_future()
169175
self.user_ws_future = asyncio.ensure_future(
170-
self.websocket_handler(os.path.join(BinanceWebsocket.WS_URL, 'ws', key), self.user_info_cb))
176+
self.websocket_handler(os.path.join(self.get_url(), 'ws', key), self.user_info_cb))
171177

172178
self.user_ws_future.add_done_callback(
173179
functools.partial(self.feature_finished, reconnect_fn=functools.partial(self.start_user_info, force_reconnect=True), name='user websocket'))

Bot/FXConnector.py

+22-12
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import time
2+
13
from binance.exceptions import BinanceAPIException, BinanceOrderException
24
from retrying import retry
35

@@ -45,10 +47,11 @@ class FXConnector(Logger):
4547
ORDER_RESP_TYPE_RESULT = 'RESULT'
4648
ORDER_RESP_TYPE_FULL = 'FULL'
4749

48-
def __init__(self, key=None, secret=None):
50+
def __init__(self, key=None, secret=None, simulation=False):
4951
super().__init__()
5052
self.__key = key
5153
self.__secret = secret
54+
self._simulation = simulation
5255
self._client = None #Client(key, secret)
5356
self.bs: BinanceWebsocket = None
5457

@@ -59,10 +62,17 @@ def __init__(self, key=None, secret=None):
5962
@property
6063
def client(self):
6164
if not self._client:
62-
self._client = Client(self.__key, self.__secret)
65+
self._client = Client(self.__key, self.__secret, testnet=self._simulation)
66+
self.test_connectivity()
6367

6468
return self._client
6569

70+
71+
def test_connectivity(self):
72+
res = self.get_server_time()
73+
self.client.timestamp_offset = res['serverTime'] - int(time.time() * 1000)
74+
75+
6676
def listen_symbols(self, symbols, on_ticker_received, user_data_handler):
6777
self.bs = BinanceWebsocket(self.client)
6878
self.bs.start_ticker(symbols, on_ticker_received)
@@ -180,16 +190,16 @@ def create_stop_order(self, sym, side, stop_price, price, volume):
180190
stopPrice=FXConnector.format_number(stop_price),
181191
price=FXConnector.format_number(price))
182192

183-
# @retry(stop_max_attempt_number=MAX_ATTEMPTS, wait_fixed=DELAY)
184-
def create_test_stop_order(self, sym, side, price, volume):
185-
return self.client.create_test_order(
186-
symbol=sym,
187-
side=side,
188-
type=FXConnector.ORDER_TYPE_STOP_LOSS_LIMIT,
189-
timeInForce=FXConnector.TIME_IN_FORCE_GTC,
190-
quantity=FXConnector.format_number(volume),
191-
stopPrice=FXConnector.format_number(price),
192-
price=FXConnector.format_number(price))
193+
# # @retry(stop_max_attempt_number=MAX_ATTEMPTS, wait_fixed=DELAY)
194+
# def create_test_stop_order(self, sym, side, price, volume):
195+
# return self.client.create_test_order(
196+
# symbol=sym,
197+
# side=side,
198+
# type=FXConnector.ORDER_TYPE_STOP_LOSS_LIMIT,
199+
# timeInForce=FXConnector.TIME_IN_FORCE_GTC,
200+
# quantity=FXConnector.format_number(volume),
201+
# stopPrice=FXConnector.format_number(price),
202+
# price=FXConnector.format_number(price))
193203

194204
@retry(**DEFAULT_RETRY_SETTINGS)
195205
def get_balance(self, asset):

Bot/Strategy/TradingStrategy.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ def __init__(self,
2525
self.fx = fx
2626
self.balance: Balance = balance if balance else Balance()
2727
self._exchange_info = None
28-
self.simulate = Utils.s2b(environ.get("SIMULTATE", False))
28+
self.simulate = Utils.is_simulation()
2929
self.trade_updated = trade_updated
3030
self.last_execution_price = 0
3131
self.paused = False

ConsoleLauncher.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ def start_bot(self):
6969
api_path = os.path.join(self.config_path, 'api.json')
7070
key, secret = self.get_exchange_creds(api_path)
7171

72-
self.fx = FXConnector(key, secret)
72+
self.fx = FXConnector(key, secret, Utils.is_simulation())
7373

7474
self.trade_handler = TradeHandler(
7575
trades,

Utils/Utils.py

+3
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,6 @@ def s2b(s):
1818
if s.lower() in ['true', 'yes', '1']:
1919
return True
2020
return False
21+
22+
def is_simulation():
23+
return s2b(os.environ.get("SIMULTATE", False))

admin/angular.json

+18-1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,20 @@
5353
"maximumWarning": "6kb"
5454
}
5555
]
56+
},
57+
"test": {
58+
"fileReplacements": [
59+
{
60+
"replace": "src/environments/environment.ts",
61+
"with": "src/environments/environment.test.ts"
62+
}
63+
],
64+
"budgets": [
65+
{
66+
"type": "anyComponentStyle",
67+
"maximumWarning": "6kb"
68+
}
69+
]
5670
}
5771
}
5872
},
@@ -64,6 +78,9 @@
6478
"configurations": {
6579
"production": {
6680
"browserTarget": "admin:build:production"
81+
},
82+
"test": {
83+
"browserTarget": "admin:build:test"
6784
}
6885
}
6986
},
@@ -129,4 +146,4 @@
129146
}
130147
},
131148
"defaultProject": "admin"
132-
}
149+
}

admin/src/app/trade-details/exit-details.component.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
<mat-form-field class="full-width exit-target-input" appearance="standard">
4040
<mat-label>Volume</mat-label>
4141
<input matInput type="text" id="targetPrice" name="targetPrice" [(ngModel)]="exitTarget.vol">
42-
<mat-hint *ngIf="exitTarget.calculated_volume">{{exitTarget.calculated_volume | number: '1.0-8'}}</mat-hint>
42+
<mat-hint *ngIf="exitTarget.calculated_volume" matTooltip="Calculated Volume">{{exitTarget.calculated_volume | number: '1.0-8'}}</mat-hint>
4343
</mat-form-field>
4444
</td>
4545
<td>

admin/src/app/trade-details/sl-details.component.html

+5-4
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,18 @@
1616
<td><mat-checkbox [(ngModel)]="trade.stoploss.isTrailing">Trailing</mat-checkbox></td>
1717
<td><mat-form-field class="full-width exit-target-input" appearance="standard">
1818
<mat-label>Threshold</mat-label>
19-
<input matInput type="text" id="targetPrice" name="targetPrice" [(ngModel)]="trade.stoploss.threshold" [disabled]="trade.stoploss.isFixed()">
19+
<input matInput type="text" id="th" name="th" [(ngModel)]="trade.stoploss.threshold" [disabled]="trade.stoploss.isFixed()">
2020
</mat-form-field>
2121
</td>
2222
<td><mat-form-field class="full-width exit-target-input" appearance="standard">
2323
<mat-label>Price</mat-label>
24-
<input matInput type="text" id="targetPrice" name="targetPrice" [(ngModel)]="trade.stoploss.initial_target.price">
24+
<input matInput type="text" id="tp" name="tp" [(ngModel)]="trade.stoploss.initial_target.price">
2525
</mat-form-field></td>
2626
<td>
2727
<mat-form-field class="full-width exit-target-input" appearance="standard">
2828
<mat-label>Volume</mat-label>
29-
<input matInput type="text" id="targetPrice" name="targetPrice" [(ngModel)]="trade.stoploss.initial_target.vol">
29+
<input matInput type="text" id="vol" name="vol" [(ngModel)]="trade.stoploss.initial_target.vol">
30+
<mat-hint *ngIf="trade.stoploss.initial_target.calculated_volume" matTooltip="Calculated Volume">{{trade.stoploss.initial_target.calculated_volume | number: '1.0-8'}}</mat-hint>
3031
</mat-form-field>
3132
</td>
3233
<td>
@@ -36,5 +37,5 @@
3637
</td>
3738
</tr>
3839
</table>
39-
40+
4041
</mat-expansion-panel>

admin/src/app/trade-details/trade-details.component.html

+9-8
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ <h1 mat-dialog-title>
1010
<mat-icon mat-list-icon>close</mat-icon>
1111
</button> -->
1212
</mat-toolbar>
13-
13+
1414
</h1>
1515

16-
17-
<div mat-dialog-content *ngIf="trade" class="trade-section">
16+
17+
<div mat-dialog-content *ngIf="trade" class="trade-section">
1818
<mat-accordion class="exp-headers-align" multi class="accordeon">
1919
<mat-expansion-panel expanded>
2020
<mat-expansion-panel-header>
@@ -29,16 +29,16 @@ <h1 mat-dialog-title>
2929
<p><span>ID: </span><span><strong>{{trade.id}}</strong></span></p>
3030
<table class="full-width" cellspacing="0"><tr>
3131
<td><app-auto-complete #symAutoComplete
32-
[fieldCtrl]="myControl"
32+
[fieldCtrl]="myControl"
3333
[placeHolder]="'ETHUSDC'"
3434
[appearance]="'standard'"
3535
[smartList]="symbols"
3636
[label] = "'Trading Pair'"
37-
(optionSelected)="onSymbolSelected($event)"
37+
(optionSelected)="onSymbolSelected($event)"
3838
[model]="trade.symbol"
3939
[class]="'full-width'"
4040
[disabled]="!mode.isCreate()"
41-
41+
4242
>
4343
</app-auto-complete></td>
4444
<td><mat-form-field class="full-width" appearance="standard">
@@ -71,14 +71,15 @@ <h1 mat-dialog-title>
7171
</table>
7272
</div>
7373
</mat-expansion-panel>
74-
74+
7575
<app-entry-details [trade]="trade" [mode]="mode"></app-entry-details>
7676
<app-exit-details [trade]="trade" [mode]="mode"></app-exit-details>
7777
<app-sl-details [trade]="trade" [mode]="mode"></app-sl-details>
7878
</mat-accordion>
7979
</div>
80+
<mat-progress-bar #progress mode="query" *ngIf="showProgress"></mat-progress-bar>
8081
<div mat-dialog-actions align="end">
81-
<button mat-button type="submit" color="primary" (click)="confirm()" *ngIf="!mode.isView()" [disabled]="myControl.errors && mode.isCreate()" >{{ mode.isCreate()? "Create":"Save" }}</button>
82+
<button mat-button type="submit" color="primary" (click)="showProgress = true; confirm()" *ngIf="!mode.isView()" [disabled]="myControl.errors && mode.isCreate()" >{{ mode.isCreate()? "Create":"Save" }}</button>
8283
<button mat-button mat-dialog-close="cancel" color="primary">Cancel</button>
8384
</div>
8485
</form>

admin/src/app/trade-details/trade-details.component.ts

+9-8
Original file line numberDiff line numberDiff line change
@@ -30,17 +30,18 @@ export class TradeDetailsComponent implements OnInit {
3030

3131
// trade$: Observable<TradeDetails>;
3232
trade: TradeDetails;
33-
33+
3434
tradeId: string;
3535
mode: Mode;
3636
// trade: TradeDetails;
3737
priceInfo: BinancePriceResult;
3838

3939
exchangeInfo: Map<string, string>;
4040
symbols: string[] = [];
41+
public showProgress: Boolean = false;
4142

4243
myControl = new FormControl('', Validators.required);
43-
44+
4445
// private config = {
4546
// class: 'modal-lg',
4647
// keyboard: false,
@@ -72,7 +73,7 @@ export class TradeDetailsComponent implements OnInit {
7273
});
7374
// tdDialog.afterClosed().subscribe(result => {
7475
// console.log(`Dialog result: ${result}`);
75-
// });
76+
// });
7677
}
7778

7879
ngOnInit() {
@@ -83,7 +84,7 @@ export class TradeDetailsComponent implements OnInit {
8384

8485
if (this.mode.isCreate()) {
8586
this.trade = new TradeDetails(true);
86-
87+
8788
this.api.getExchangeInfo().subscribe(
8889
res => {
8990
this.exchangeInfo = new Map<string, string>();
@@ -107,7 +108,7 @@ export class TradeDetailsComponent implements OnInit {
107108
}
108109
}
109110

110-
confirm() {
111+
confirm() {
111112
this.api.addTrade(this.trade).subscribe(
112113
res => {
113114
if (this.mode.isCreate() || this.mode.isEdit()) {
@@ -137,7 +138,7 @@ export class TradeDetailsComponent implements OnInit {
137138
// this.priceInfo = price;
138139
// }
139140

140-
// as entry doesn't allow to choose side now, changing side on the
141+
// as entry doesn't allow to choose side now, changing side on the
141142
// parent level will set opposite side for the entry
142143
onSideChanged(event): void {
143144
if (this.trade.entry){
@@ -152,11 +153,11 @@ export class TradeDetailsComponent implements OnInit {
152153
if (this.mode.isCreate()) {
153154

154155
let asset = this.exchangeInfo.get(symbol)
155-
156+
156157
if (this.trade.symbol !== symbol) {
157158
this.trade.asset = asset;
158159
this.trade.symbol = symbol;
159-
160+
160161
this.api.bookTicker(symbol).subscribe(
161162
result => { this.priceInfo = result;}
162163
)

requirements.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
numpy
2-
python-binance==1.0.3
2+
python-binance==1.0.10
33
retrying==1.3.3
44
boto3==1.17.62
55
websockets==9.0.1

0 commit comments

Comments
 (0)