bbstrader 0.3.5__py3-none-any.whl → 0.3.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of bbstrader might be problematic. Click here for more details.
- bbstrader/__init__.py +10 -1
- bbstrader/__main__.py +5 -0
- bbstrader/apps/_copier.py +3 -3
- bbstrader/btengine/strategy.py +113 -38
- bbstrader/metatrader/account.py +51 -26
- bbstrader/metatrader/analysis.py +30 -16
- bbstrader/metatrader/copier.py +75 -40
- bbstrader/metatrader/trade.py +29 -39
- bbstrader/metatrader/utils.py +5 -4
- bbstrader/models/nlp.py +83 -66
- bbstrader/trading/execution.py +39 -22
- bbstrader/tseries.py +103 -127
- {bbstrader-0.3.5.dist-info → bbstrader-0.3.6.dist-info}/METADATA +7 -21
- {bbstrader-0.3.5.dist-info → bbstrader-0.3.6.dist-info}/RECORD +31 -18
- bbstrader-0.3.6.dist-info/top_level.txt +3 -0
- docs/conf.py +56 -0
- tests/__init__.py +0 -0
- tests/engine/__init__.py +1 -0
- tests/engine/test_backtest.py +58 -0
- tests/engine/test_data.py +536 -0
- tests/engine/test_events.py +300 -0
- tests/engine/test_execution.py +219 -0
- tests/engine/test_portfolio.py +307 -0
- tests/metatrader/__init__.py +0 -0
- tests/metatrader/test_account.py +1769 -0
- tests/metatrader/test_rates.py +292 -0
- tests/metatrader/test_risk_management.py +700 -0
- tests/metatrader/test_trade.py +439 -0
- bbstrader-0.3.5.dist-info/top_level.txt +0 -1
- {bbstrader-0.3.5.dist-info → bbstrader-0.3.6.dist-info}/WHEEL +0 -0
- {bbstrader-0.3.5.dist-info → bbstrader-0.3.6.dist-info}/entry_points.txt +0 -0
- {bbstrader-0.3.5.dist-info → bbstrader-0.3.6.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
from unittest.mock import MagicMock, patch
|
|
4
|
+
|
|
5
|
+
from bbstrader.metatrader.trade import (
|
|
6
|
+
Trade,
|
|
7
|
+
TradeAction,
|
|
8
|
+
TradeSignal,
|
|
9
|
+
TradingMode,
|
|
10
|
+
create_trade_instance,
|
|
11
|
+
generate_signal,
|
|
12
|
+
)
|
|
13
|
+
from bbstrader.metatrader.utils import (
|
|
14
|
+
AccountInfo,
|
|
15
|
+
SymbolInfo,
|
|
16
|
+
TickInfo,
|
|
17
|
+
TradeOrder,
|
|
18
|
+
TradePosition,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class TestTrade(unittest.TestCase):
|
|
23
|
+
def setUp(self):
|
|
24
|
+
"""Set up the test environment."""
|
|
25
|
+
self.mt5_patcher = patch("bbstrader.metatrader.trade.Mt5")
|
|
26
|
+
self.mock_mt5 = self.mt5_patcher.start()
|
|
27
|
+
self.addCleanup(self.mt5_patcher.stop)
|
|
28
|
+
|
|
29
|
+
self.logger_patcher = patch("bbstrader.metatrader.trade.log")
|
|
30
|
+
self.mock_logger = self.logger_patcher.start()
|
|
31
|
+
self.addCleanup(self.logger_patcher.stop)
|
|
32
|
+
|
|
33
|
+
patch(
|
|
34
|
+
"bbstrader.metatrader.risk.RiskManagement.__init__", return_value=None
|
|
35
|
+
).start()
|
|
36
|
+
self.addCleanup(patch.stopall)
|
|
37
|
+
|
|
38
|
+
patch("bbstrader.metatrader.trade.check_mt5_connection").start()
|
|
39
|
+
patch("bbstrader.metatrader.trade.Trade.select_symbol").start()
|
|
40
|
+
patch("bbstrader.metatrader.trade.Trade.prepare_symbol").start()
|
|
41
|
+
|
|
42
|
+
self.account_info = AccountInfo(
|
|
43
|
+
login=12345,
|
|
44
|
+
balance=10000.0,
|
|
45
|
+
equity=10000.0,
|
|
46
|
+
currency="USD",
|
|
47
|
+
name="Test Account",
|
|
48
|
+
server="Test Server",
|
|
49
|
+
leverage=100,
|
|
50
|
+
trade_mode=0,
|
|
51
|
+
limit_orders=0,
|
|
52
|
+
margin_so_mode=0,
|
|
53
|
+
trade_allowed=True,
|
|
54
|
+
trade_expert=True,
|
|
55
|
+
margin_mode=0,
|
|
56
|
+
currency_digits=2,
|
|
57
|
+
fifo_close=False,
|
|
58
|
+
credit=0.0,
|
|
59
|
+
profit=0.0,
|
|
60
|
+
margin=0.0,
|
|
61
|
+
margin_free=10000.0,
|
|
62
|
+
margin_level=0.0,
|
|
63
|
+
margin_so_call=0.0,
|
|
64
|
+
margin_so_so=0.0,
|
|
65
|
+
margin_initial=0.0,
|
|
66
|
+
margin_maintenance=0.0,
|
|
67
|
+
assets=0.0,
|
|
68
|
+
liabilities=0.0,
|
|
69
|
+
commission_blocked=0.0,
|
|
70
|
+
company="MetaQuotes",
|
|
71
|
+
)
|
|
72
|
+
self.symbol_info = SymbolInfo(
|
|
73
|
+
name="EURUSD",
|
|
74
|
+
visible=True,
|
|
75
|
+
point=0.00001,
|
|
76
|
+
digits=5,
|
|
77
|
+
spread=10,
|
|
78
|
+
trade_tick_value=1.0,
|
|
79
|
+
trade_tick_size=0.00001,
|
|
80
|
+
trade_contract_size=100000,
|
|
81
|
+
volume_min=0.01,
|
|
82
|
+
volume_max=100.0,
|
|
83
|
+
volume_step=0.01,
|
|
84
|
+
bid=1.10000,
|
|
85
|
+
ask=1.10010,
|
|
86
|
+
time=datetime.now(),
|
|
87
|
+
custom=False,
|
|
88
|
+
chart_mode=0,
|
|
89
|
+
select=True,
|
|
90
|
+
session_deals=0,
|
|
91
|
+
session_buy_orders=0,
|
|
92
|
+
session_sell_orders=0,
|
|
93
|
+
volume=0,
|
|
94
|
+
volumehigh=0,
|
|
95
|
+
volumelow=0,
|
|
96
|
+
bidhigh=0,
|
|
97
|
+
bidlow=0,
|
|
98
|
+
askhigh=0,
|
|
99
|
+
asklow=0,
|
|
100
|
+
last=0,
|
|
101
|
+
lasthigh=0,
|
|
102
|
+
lastlow=0,
|
|
103
|
+
volume_real=0,
|
|
104
|
+
volumehigh_real=0,
|
|
105
|
+
volumelow_real=0,
|
|
106
|
+
option_strike=0,
|
|
107
|
+
trade_tick_value_profit=1.0,
|
|
108
|
+
trade_tick_value_loss=1.0,
|
|
109
|
+
trade_stops_level=0,
|
|
110
|
+
trade_freeze_level=0,
|
|
111
|
+
trade_exemode=0,
|
|
112
|
+
swap_mode=0,
|
|
113
|
+
swap_rollover3days=0,
|
|
114
|
+
margin_hedged_use_leg=False,
|
|
115
|
+
expiration_mode=0,
|
|
116
|
+
filling_mode=0,
|
|
117
|
+
order_mode=0,
|
|
118
|
+
order_gtc_mode=0,
|
|
119
|
+
option_mode=0,
|
|
120
|
+
option_right=0,
|
|
121
|
+
margin_initial=0,
|
|
122
|
+
margin_maintenance=0,
|
|
123
|
+
session_volume=0,
|
|
124
|
+
session_turnover=0,
|
|
125
|
+
session_interest=0,
|
|
126
|
+
session_buy_orders_volume=0,
|
|
127
|
+
session_sell_orders_volume=0,
|
|
128
|
+
session_open=0,
|
|
129
|
+
session_close=0,
|
|
130
|
+
session_aw=0,
|
|
131
|
+
session_price_settlement=0,
|
|
132
|
+
session_price_limit_min=0,
|
|
133
|
+
session_price_limit_max=0,
|
|
134
|
+
margin_hedged=0,
|
|
135
|
+
price_change=0,
|
|
136
|
+
price_volatility=0,
|
|
137
|
+
price_theoretical=0,
|
|
138
|
+
price_greeks_delta=0,
|
|
139
|
+
price_greeks_theta=0,
|
|
140
|
+
price_greeks_gamma=0,
|
|
141
|
+
price_greeks_vega=0,
|
|
142
|
+
price_greeks_rho=0,
|
|
143
|
+
price_greeks_omega=0,
|
|
144
|
+
price_sensitivity=0,
|
|
145
|
+
basis="",
|
|
146
|
+
category="",
|
|
147
|
+
currency_base="EUR",
|
|
148
|
+
currency_profit="USD",
|
|
149
|
+
currency_margin="EUR",
|
|
150
|
+
bank="",
|
|
151
|
+
description="Euro vs US Dollar",
|
|
152
|
+
exchange="",
|
|
153
|
+
formula="",
|
|
154
|
+
isin="",
|
|
155
|
+
page="",
|
|
156
|
+
path="Forex\\Majors\\EURUSD",
|
|
157
|
+
start_time=0,
|
|
158
|
+
expiration_time=0,
|
|
159
|
+
spread_float=True,
|
|
160
|
+
ticks_bookdepth=0,
|
|
161
|
+
trade_calc_mode=0,
|
|
162
|
+
trade_mode=0,
|
|
163
|
+
trade_accrued_interest=0.0,
|
|
164
|
+
trade_face_value=0.0,
|
|
165
|
+
trade_liquidity_rate=0.0,
|
|
166
|
+
volume_limit=0.0,
|
|
167
|
+
swap_long=0.0,
|
|
168
|
+
swap_short=0.0,
|
|
169
|
+
)
|
|
170
|
+
self.tick_info = TickInfo(
|
|
171
|
+
time=datetime.now(),
|
|
172
|
+
bid=1.10000,
|
|
173
|
+
ask=1.10010,
|
|
174
|
+
last=1.10005,
|
|
175
|
+
volume=100,
|
|
176
|
+
time_msc=int(datetime.now().timestamp() * 1000),
|
|
177
|
+
flags=6,
|
|
178
|
+
volume_real=100.0,
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
self.trade = Trade(symbol="EURUSD", expert_id=98181105, verbose=False)
|
|
182
|
+
|
|
183
|
+
# Manually set attributes from the patched RiskManagement parent class
|
|
184
|
+
self.trade.symbol_info = self.symbol_info
|
|
185
|
+
self.trade.account_leverage = True
|
|
186
|
+
self.trade.be = 10
|
|
187
|
+
self.trade.max_risk = 5.0
|
|
188
|
+
self.trade.rr = 2.0
|
|
189
|
+
self.trade.copy_mode = False
|
|
190
|
+
|
|
191
|
+
# Patch instance methods to return mock data
|
|
192
|
+
patch.object(
|
|
193
|
+
self.trade, "get_account_info", return_value=self.account_info
|
|
194
|
+
).start()
|
|
195
|
+
patch.object(
|
|
196
|
+
self.trade, "get_symbol_info", return_value=self.symbol_info
|
|
197
|
+
).start()
|
|
198
|
+
patch.object(self.trade, "get_tick_info", return_value=self.tick_info).start()
|
|
199
|
+
patch.object(self.trade, "get_lot", return_value=0.1).start()
|
|
200
|
+
patch.object(self.trade, "get_stop_loss", return_value=200).start()
|
|
201
|
+
patch.object(self.trade, "get_take_profit", return_value=300).start()
|
|
202
|
+
patch.object(
|
|
203
|
+
self.trade,
|
|
204
|
+
"send_order",
|
|
205
|
+
return_value=MagicMock(retcode=self.mock_mt5.TRADE_RETCODE_DONE, order=123),
|
|
206
|
+
).start()
|
|
207
|
+
# This patch is crucial to prevent the UnboundLocalError in close_request
|
|
208
|
+
patch.object(self.trade, "check_order", return_value=True).start()
|
|
209
|
+
|
|
210
|
+
def tearDown(self):
|
|
211
|
+
"""This method is no longer needed as addCleanup handles stopping patches."""
|
|
212
|
+
pass
|
|
213
|
+
|
|
214
|
+
def test_trade_signal_initialization(self):
|
|
215
|
+
"""Test TradeSignal dataclass initialization and validation."""
|
|
216
|
+
signal = TradeSignal(id=1, symbol="EURUSD", action=TradeAction.BUY, price=1.2)
|
|
217
|
+
self.assertEqual(signal.id, 1)
|
|
218
|
+
self.assertEqual(signal.action, TradeAction.BUY)
|
|
219
|
+
|
|
220
|
+
with self.assertRaises(TypeError):
|
|
221
|
+
TradeSignal(id=1, symbol="EURUSD", action="BUY", price=1.2)
|
|
222
|
+
|
|
223
|
+
with self.assertRaises(ValueError):
|
|
224
|
+
TradeSignal(id=1, symbol="EURUSD", action=TradeAction.BUY, stoplimit=1.2)
|
|
225
|
+
|
|
226
|
+
def test_generate_signal(self):
|
|
227
|
+
"""Test the generate_signal factory function."""
|
|
228
|
+
signal = generate_signal(
|
|
229
|
+
id=1, symbol="EURUSD", action=TradeAction.SELL, price=1.3
|
|
230
|
+
)
|
|
231
|
+
self.assertIsInstance(signal, TradeSignal)
|
|
232
|
+
self.assertEqual(signal.price, 1.3)
|
|
233
|
+
|
|
234
|
+
def test_trading_mode_enum(self):
|
|
235
|
+
"""Test the TradingMode enum."""
|
|
236
|
+
self.assertTrue(TradingMode.BACKTEST.isbacktest())
|
|
237
|
+
self.assertFalse(TradingMode.LIVE.isbacktest())
|
|
238
|
+
self.assertTrue(TradingMode.LIVE.islive())
|
|
239
|
+
self.assertFalse(TradingMode.BACKTEST.islive())
|
|
240
|
+
|
|
241
|
+
@patch("bbstrader.metatrader.trade.tabulate")
|
|
242
|
+
@patch("builtins.print")
|
|
243
|
+
def test_summary(self, mock_print, mock_tabulate):
|
|
244
|
+
"""Test the summary method."""
|
|
245
|
+
self.trade.summary()
|
|
246
|
+
mock_tabulate.assert_called_once()
|
|
247
|
+
mock_print.assert_called()
|
|
248
|
+
|
|
249
|
+
@patch("bbstrader.metatrader.trade.tabulate")
|
|
250
|
+
@patch("builtins.print")
|
|
251
|
+
def test_risk_management_summary(self, mock_print, mock_tabulate):
|
|
252
|
+
"""Test the risk_managment method."""
|
|
253
|
+
with (
|
|
254
|
+
patch.object(
|
|
255
|
+
self.trade, "get_stats", return_value=({}, {"total_profit": 100})
|
|
256
|
+
),
|
|
257
|
+
patch.object(
|
|
258
|
+
self.trade,
|
|
259
|
+
"currency_risk",
|
|
260
|
+
return_value={"trade_loss": 10, "trade_profit": 20},
|
|
261
|
+
),
|
|
262
|
+
patch.object(self.trade, "get_currency_rates", return_value={"mc": "EUR"}),
|
|
263
|
+
patch.object(self.trade, "is_risk_ok", return_value=True),
|
|
264
|
+
patch.object(self.trade, "risk_level", return_value=1.0),
|
|
265
|
+
patch.object(self.trade, "get_leverage", return_value="1:100"),
|
|
266
|
+
patch.object(self.trade, "volume", return_value=0.1),
|
|
267
|
+
patch.object(self.trade, "get_currency_risk", return_value=100),
|
|
268
|
+
patch.object(self.trade, "expected_profit", return_value=200),
|
|
269
|
+
patch.object(self.trade, "get_break_even", return_value=50),
|
|
270
|
+
patch.object(self.trade, "get_deviation", return_value=20),
|
|
271
|
+
patch.object(self.trade, "get_minutes", return_value=60),
|
|
272
|
+
patch.object(self.trade, "max_trade", return_value=10),
|
|
273
|
+
):
|
|
274
|
+
self.trade.risk_managment()
|
|
275
|
+
mock_tabulate.assert_called_once()
|
|
276
|
+
mock_print.assert_called()
|
|
277
|
+
|
|
278
|
+
@patch("pandas.DataFrame.to_csv")
|
|
279
|
+
@patch("os.makedirs")
|
|
280
|
+
def test_statistics(self, mock_makedirs, mock_to_csv):
|
|
281
|
+
"""Test the statistics method."""
|
|
282
|
+
stats1 = {
|
|
283
|
+
"deals": 1,
|
|
284
|
+
"profit": 100,
|
|
285
|
+
"win_trades": 1,
|
|
286
|
+
"loss_trades": 0,
|
|
287
|
+
"total_fees": -10,
|
|
288
|
+
"average_fee": -10,
|
|
289
|
+
"win_rate": 100,
|
|
290
|
+
}
|
|
291
|
+
stats2 = {"total_profit": 90, "profitability": "Yes"}
|
|
292
|
+
with (
|
|
293
|
+
patch.object(self.trade, "get_stats", return_value=(stats1, stats2)),
|
|
294
|
+
patch.object(self.trade, "sharpe", return_value=1.5),
|
|
295
|
+
patch.object(self.trade, "get_currency_risk", return_value=100),
|
|
296
|
+
patch.object(self.trade, "expected_profit", return_value=200),
|
|
297
|
+
):
|
|
298
|
+
self.trade.statistics(save=True, dir="test_stats")
|
|
299
|
+
mock_makedirs.assert_called_with("test_stats", exist_ok=True)
|
|
300
|
+
mock_to_csv.assert_called_once()
|
|
301
|
+
|
|
302
|
+
def test_open_buy_position(self):
|
|
303
|
+
"""Test opening a buy position."""
|
|
304
|
+
with patch.object(self.trade, "check", return_value=True):
|
|
305
|
+
result = self.trade.open_buy_position(action="BMKT")
|
|
306
|
+
self.assertTrue(result)
|
|
307
|
+
self.trade.send_order.assert_called()
|
|
308
|
+
|
|
309
|
+
def test_open_sell_position(self):
|
|
310
|
+
"""Test opening a sell position."""
|
|
311
|
+
with patch.object(self.trade, "check", return_value=True):
|
|
312
|
+
result = self.trade.open_sell_position(action="SMKT")
|
|
313
|
+
self.assertTrue(result)
|
|
314
|
+
self.trade.send_order.assert_called()
|
|
315
|
+
|
|
316
|
+
def test_open_position(self):
|
|
317
|
+
"""Test the generic open_position method."""
|
|
318
|
+
with patch.object(self.trade, "open_buy_position") as mock_buy:
|
|
319
|
+
self.trade.open_position(action="BMKT")
|
|
320
|
+
mock_buy.assert_called_once()
|
|
321
|
+
|
|
322
|
+
with patch.object(self.trade, "open_sell_position") as mock_sell:
|
|
323
|
+
self.trade.open_position(action="SMKT")
|
|
324
|
+
mock_sell.assert_called_once()
|
|
325
|
+
|
|
326
|
+
with self.assertRaises(ValueError):
|
|
327
|
+
self.trade.open_position(action="INVALID_ACTION")
|
|
328
|
+
|
|
329
|
+
def test_close_position(self):
|
|
330
|
+
"""Test closing a position."""
|
|
331
|
+
position = self._get_mock_position(ticket=123)
|
|
332
|
+
with patch.object(self.trade, "get_positions", return_value=[position]):
|
|
333
|
+
result = self.trade.close_position(ticket=123)
|
|
334
|
+
self.assertTrue(result)
|
|
335
|
+
self.trade.send_order.assert_called()
|
|
336
|
+
|
|
337
|
+
def test_close_order(self):
|
|
338
|
+
"""Test closing an order."""
|
|
339
|
+
with patch.object(
|
|
340
|
+
self.trade, "close_request", return_value=True
|
|
341
|
+
) as mock_close_request:
|
|
342
|
+
result = self.trade.close_order(ticket=456)
|
|
343
|
+
self.assertTrue(result)
|
|
344
|
+
mock_close_request.assert_called_once()
|
|
345
|
+
|
|
346
|
+
def test_modify_order(self):
|
|
347
|
+
"""Test modifying an order."""
|
|
348
|
+
order = self._get_mock_order(ticket=789)
|
|
349
|
+
with (
|
|
350
|
+
patch.object(self.trade, "get_orders", return_value=[order]),
|
|
351
|
+
patch.object(self.trade, "check_order", return_value=True),
|
|
352
|
+
):
|
|
353
|
+
self.trade.modify_order(ticket=789, price=1.15)
|
|
354
|
+
self.trade.send_order.assert_called()
|
|
355
|
+
call_args = self.trade.send_order.call_args[0][0]
|
|
356
|
+
self.assertEqual(call_args["price"], 1.15)
|
|
357
|
+
|
|
358
|
+
def test_create_trade_instance(self):
|
|
359
|
+
"""Test the create_trade_instance factory function."""
|
|
360
|
+
with patch("bbstrader.metatrader.trade.Trade") as mock_trade:
|
|
361
|
+
params = {"expert_id": 123}
|
|
362
|
+
symbols = ["EURUSD", "GBPUSD"]
|
|
363
|
+
instances = create_trade_instance(symbols, params)
|
|
364
|
+
self.assertEqual(len(instances), 2)
|
|
365
|
+
self.assertIn("EURUSD", instances)
|
|
366
|
+
self.assertIn("GBPUSD", instances)
|
|
367
|
+
self.assertEqual(mock_trade.call_count, 2)
|
|
368
|
+
|
|
369
|
+
def _get_mock_position(
|
|
370
|
+
self,
|
|
371
|
+
ticket=1,
|
|
372
|
+
symbol="EURUSD",
|
|
373
|
+
volume=0.1,
|
|
374
|
+
price_open=1.1,
|
|
375
|
+
type=0,
|
|
376
|
+
magic=98181105,
|
|
377
|
+
profit=0.0,
|
|
378
|
+
):
|
|
379
|
+
return TradePosition(
|
|
380
|
+
ticket=ticket,
|
|
381
|
+
time=int(datetime.now().timestamp()),
|
|
382
|
+
time_msc=0,
|
|
383
|
+
time_update=0,
|
|
384
|
+
time_update_msc=0,
|
|
385
|
+
type=type,
|
|
386
|
+
magic=magic,
|
|
387
|
+
identifier=0,
|
|
388
|
+
reason=0,
|
|
389
|
+
volume=volume,
|
|
390
|
+
price_open=price_open,
|
|
391
|
+
sl=0,
|
|
392
|
+
tp=0,
|
|
393
|
+
price_current=price_open + 0.001,
|
|
394
|
+
swap=0,
|
|
395
|
+
profit=profit,
|
|
396
|
+
symbol=symbol,
|
|
397
|
+
comment="test",
|
|
398
|
+
external_id="",
|
|
399
|
+
)
|
|
400
|
+
|
|
401
|
+
def _get_mock_order(
|
|
402
|
+
self,
|
|
403
|
+
ticket=1,
|
|
404
|
+
symbol="EURUSD",
|
|
405
|
+
price_open=1.1,
|
|
406
|
+
volume_initial=0.1,
|
|
407
|
+
type=0,
|
|
408
|
+
magic=98181105,
|
|
409
|
+
):
|
|
410
|
+
return TradeOrder(
|
|
411
|
+
ticket=ticket,
|
|
412
|
+
time_setup=int(datetime.now().timestamp()),
|
|
413
|
+
time_setup_msc=0,
|
|
414
|
+
time_done=0,
|
|
415
|
+
time_done_msc=0,
|
|
416
|
+
time_expiration=0,
|
|
417
|
+
type=type,
|
|
418
|
+
type_time=0,
|
|
419
|
+
type_filling=0,
|
|
420
|
+
state=0,
|
|
421
|
+
magic=magic,
|
|
422
|
+
position_id=0,
|
|
423
|
+
position_by_id=0,
|
|
424
|
+
reason=0,
|
|
425
|
+
volume_initial=volume_initial,
|
|
426
|
+
volume_current=0.1,
|
|
427
|
+
price_open=price_open,
|
|
428
|
+
sl=0,
|
|
429
|
+
tp=0,
|
|
430
|
+
price_current=price_open,
|
|
431
|
+
price_stoplimit=0,
|
|
432
|
+
symbol=symbol,
|
|
433
|
+
comment="test",
|
|
434
|
+
external_id="",
|
|
435
|
+
)
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
if __name__ == "__main__":
|
|
439
|
+
unittest.main()
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
bbstrader
|
|
File without changes
|
|
File without changes
|
|
File without changes
|