Skip to content

Commit a1470d6

Browse files
committed
Move ratio basket plot, refactor
1 parent 936d1f4 commit a1470d6

File tree

4 files changed

+122
-416
lines changed

4 files changed

+122
-416
lines changed

Trading/live/range/filter_ranging_stocks.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,6 @@ def reprocess_item(self, item, iteration_index, client) -> XTB_STOCK_SYMBOLS_DIC
142142
for candidate in candidates:
143143
LOGGER.info(f"{candidate[0]}: {candidate[1]:.3f}")
144144

145-
146145
# 2024-12-17 14:42:28 - INFO - filter_ranging_stocks - STRO.US: 2.467
147146
# 2024-12-17 14:42:28 - INFO - filter_ranging_stocks - OERL.CH_9: 1.427
148147
# 2024-12-17 14:42:28 - INFO - filter_ranging_stocks - JAMF.US: 1.318

Trading/live/ratio/backtest.py

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
from Trading.utils.ratio.ratio import Ratio, DateNotFoundError, CurrentHolding
2+
from stateful_data_processor.file_rw import JsonFileRW
3+
import operator
4+
from typing import List, Optional
5+
from Trading.model.trade import (
6+
Trade,
7+
analyze_trades,
8+
StrategySummary,
9+
aggregate_analysis_results,
10+
TradesAnalysisResult,
11+
)
12+
13+
trades_file_writer = JsonFileRW(
14+
"/home/doru/personal/trading/Trading/live/ratio/trades.json"
15+
)
16+
analysis_file_writer = JsonFileRW(
17+
"/home/doru/personal/trading/Trading/live/ratio/analysis.json"
18+
)
19+
20+
def backtest_ratio(ratio: Ratio, std_scaler, logger) -> Optional[TradesAnalysisResult]:
21+
trades = []
22+
if ratio.ratio_values != ratio.calculate_ratio():
23+
raise Exception("Error in calculating ratio")
24+
25+
current_holding = CurrentHolding.NONE
26+
# for peak, entry_date in zip(peak_dict["values"], peak_dict["dates"]):
27+
index = 0
28+
while index < len(ratio.ratio_values):
29+
current_value = ratio.ratio_values[index]
30+
entry_date = ratio.dates[index]
31+
trade_tuple: List[Trade] = []
32+
#! WE SHOULD NOT LOOK AT ABS OF CURRENT VALUE - MEAN
33+
if (
34+
current_holding == CurrentHolding.NONE
35+
and abs(current_value - ratio.mean) >= std_scaler * ratio.std
36+
):
37+
logger.info(f"Found a peak at date: {entry_date}")
38+
if current_value > ratio.mean:
39+
#! At high peak, buy the denominator
40+
entry_prices = ratio.get_denominator_prices_at_date(entry_date)
41+
current_holding = CurrentHolding.DENOMINATOR
42+
d_n = ratio.denominator
43+
else:
44+
#! At low peak, buy the numerator
45+
entry_prices = ratio.get_numerator_prices_at_date(entry_date)
46+
current_holding = CurrentHolding.NUMERATOR
47+
d_n = ratio.numerator
48+
for price, sym in zip(entry_prices, d_n):
49+
if not price:
50+
raise Exception("Price is None")
51+
trade_tuple.append(
52+
Trade(cmd=0, entry_date=entry_date, open_price=price, symbol=sym)
53+
)
54+
trades.append(trade_tuple)
55+
if current_holding == CurrentHolding.NONE:
56+
index += 1
57+
continue
58+
try:
59+
if current_holding == CurrentHolding.DENOMINATOR:
60+
next_date_at_mean, index = ratio.get_next_date_cross_mean(entry_date, operator.lt)
61+
exit_prices = ratio.get_denominator_prices_at_date(next_date_at_mean)
62+
elif current_holding == CurrentHolding.NUMERATOR:
63+
next_date_at_mean, index = ratio.get_next_date_cross_mean(entry_date, operator.gt)
64+
exit_prices = ratio.get_numerator_prices_at_date(next_date_at_mean)
65+
66+
logger.info(f"Closing on: {next_date_at_mean}")
67+
for i, p in enumerate(exit_prices):
68+
trades[-1][i].exit_date = next_date_at_mean
69+
trades[-1][i].close_price = p
70+
trades[-1][i].calculate_max_drawdown_price_diff(
71+
ratio.histories[trade_tuple[i].symbol]
72+
)
73+
current_holding = CurrentHolding.NONE
74+
except DateNotFoundError:
75+
logger.info(f"No date found at mean for {entry_date}")
76+
trades.pop()
77+
break
78+
79+
tuple_analyses = []
80+
81+
for trade_tuple in trades:
82+
# if the trade_tuple does not have a closing price, we should not analyze it
83+
trade_tuple = [t for t in trade_tuple if t.close_price]
84+
# logger.info(f"Trade tuple: {trade_tuple}")
85+
analysis = analyze_trades(
86+
trade_tuple, StrategySummary(False, 1000, 1, "USD", "STC")
87+
)
88+
if analysis is None:
89+
continue
90+
tuple_analyses.append(analysis)
91+
92+
if not tuple_analyses:
93+
return None
94+
print("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++")
95+
trade_analysis_result = aggregate_analysis_results(tuple_analyses)
96+
trade_analysis_result.print()
97+
print("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++")
98+
print()
99+
100+
trades = [trade for trade_tuple in trades for trade in trade_tuple]
101+
trades_dict = dict()
102+
trades_dict["trades"] = [t.dict() for t in trades]
103+
104+
trades_file_writer.write(trades_dict)
105+
trade_analysis_result = analyze_trades(trades, StrategySummary(False, 1000, 1, "USD", "STC"))
106+
analysis_file_writer.write(trade_analysis_result.dict())
107+
return trade_analysis_result

Trading/live/ratio/ratio_basket_plot.py

Lines changed: 15 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from Trading.live.caching.caching import get_last_n_candles_from_date
1010
from typing import Optional, Dict
1111
from dotenv import load_dotenv
12+
import operator
1213
import os
1314
import logging
1415
import sys
@@ -17,24 +18,20 @@
1718
from typing import List
1819
from Trading.utils.ratio.ratio import Ratio, DateNotFoundError, CurrentHolding
1920
from Trading.utils.calculations import calculate_mean
20-
from stateful_data_processor.file_rw import JsonFileRW
21-
21+
from Trading.live.ratio.backtest import backtest_ratio
22+
from Trading.live.ratio.heap import Heap
2223

2324
def exit():
2425
sys.exit(0)
2526

26-
27-
trades_file_writer = JsonFileRW(
28-
"/home/doru/personal/trading/Trading/live/ratio/trades.json"
29-
)
30-
analysis_file_writer = JsonFileRW(
31-
"/home/doru/personal/trading/Trading/live/ratio/analysis.json"
32-
)
33-
27+
#! Used for in-memory caching
3428
HISTORIES_DICT = {}
29+
3530
DATA_SOURCE = DataSourceEnum.XTB
3631
GET_DATA_BEFORE_DATE = datetime(2025, 1, 1)
32+
STD_SCALER = 1.5
3733

34+
top_10_ratios = Heap(10, lambda x: x[0])
3835

3936
@dataclass
4037
class Criterion:
@@ -107,7 +104,6 @@ def construct_ratio(ratio: Ratio, N_DAYS: int):
107104

108105

109106
def process_ratio(ratio: Ratio, N_DAYS: int, iteration_info: str = ""):
110-
STD_SCALER = 1.5
111107
construct_ratio(ratio, N_DAYS)
112108
# MAIN_LOGGER.info(f"Calculating ratio: {ratio.numerator} / {ratio.denominator}")
113109
ratio.calculate_ratio()
@@ -144,6 +140,12 @@ def process_ratio(ratio: Ratio, N_DAYS: int, iteration_info: str = ""):
144140
print(
145141
f"Found a ratio with at least one swing per year: {ratio.numerator} / {ratio.denominator}"
146142
)
143+
144+
trade_analysis_result = backtest_ratio(ratio, STD_SCALER, MAIN_LOGGER)
145+
if trade_analysis_result:
146+
ar = trade_analysis_result.annualized_return
147+
top_10_ratios.push((ar, iteration_info, trade_analysis_result))
148+
147149
# plot_list_dates(
148150
# ratio_values,
149151
# dates,
@@ -154,104 +156,6 @@ def process_ratio(ratio: Ratio, N_DAYS: int, iteration_info: str = ""):
154156
# show_cursor=True,
155157
# )
156158

157-
trades = []
158-
if ratio_values != ratio.calculate_ratio():
159-
raise Exception("Error in calculating ratio")
160-
161-
from Trading.model.trade import (
162-
Trade,
163-
analyze_trades,
164-
StrategySummary,
165-
aggregate_analysis_results,
166-
)
167-
168-
current_holding = CurrentHolding.NONE
169-
# for peak, entry_date in zip(peak_dict["values"], peak_dict["dates"]):
170-
index = 0
171-
while index < len(ratio.ratio_values):
172-
current_value = ratio.ratio_values[index]
173-
entry_date = ratio.dates[index]
174-
trade_tuple: List[Trade] = []
175-
#! WE SHOULD NOT LOOK AT ABS OF CURRENT VALUE - MEAN
176-
if (
177-
current_holding == CurrentHolding.NONE
178-
and abs(current_value - ratio.mean) >= STD_SCALER * ratio.std
179-
):
180-
MAIN_LOGGER.info(f"Found a peak at date: {entry_date}")
181-
if current_value > ratio.mean:
182-
#! At high peak, buy the denominator
183-
entry_prices = ratio.get_denominator_prices_at_date(entry_date)
184-
current_holding = CurrentHolding.DENOMINATOR
185-
d_n = ratio.denominator
186-
else:
187-
#! At low peak, buy the numerator
188-
entry_prices = ratio.get_numerator_prices_at_date(entry_date)
189-
current_holding = CurrentHolding.NUMERATOR
190-
d_n = ratio.numerator
191-
for price, sym in zip(entry_prices, d_n):
192-
if not price:
193-
raise Exception("Price is None")
194-
trade_tuple.append(
195-
Trade(cmd=0, entry_date=entry_date, open_price=price, symbol=sym)
196-
)
197-
trades.append(trade_tuple)
198-
if current_holding == CurrentHolding.NONE:
199-
index += 1
200-
continue
201-
try:
202-
203-
next_date_at_mean, index = ratio.get_next_date_at_mean(entry_date)
204-
MAIN_LOGGER.info(f"Closing on: {next_date_at_mean}")
205-
if current_holding == CurrentHolding.DENOMINATOR:
206-
exit_prices = ratio.get_denominator_prices_at_date(next_date_at_mean)
207-
elif current_holding == CurrentHolding.NUMERATOR:
208-
exit_prices = ratio.get_numerator_prices_at_date(next_date_at_mean)
209-
for i, p in enumerate(exit_prices):
210-
trades[-1][i].exit_date = next_date_at_mean
211-
trades[-1][i].close_price = p
212-
trades[-1][i].calculate_max_drawdown_price_diff(
213-
ratio.histories[trade_tuple[i].symbol]
214-
)
215-
current_holding = CurrentHolding.NONE
216-
except DateNotFoundError:
217-
MAIN_LOGGER.info(f"No date found at mean for {entry_date}")
218-
trades.pop()
219-
break
220-
tuple_analyses = []
221-
for trade_tuple in trades:
222-
# if the trade_tuple does not have a closing price, we should not analyze it
223-
trade_tuple = [t for t in trade_tuple if t.close_price]
224-
MAIN_LOGGER.info(f"Trade tuple: {trade_tuple}")
225-
analysis = analyze_trades(
226-
trade_tuple, StrategySummary(False, 1000, 1, "USD", "STC")
227-
)
228-
if analysis is None:
229-
continue
230-
analysis.print()
231-
tuple_analyses.append(analysis)
232-
233-
plot_list_dates(
234-
ratio_values,
235-
dates,
236-
f"Iteration number {iteration_info}",
237-
"Ratio Value",
238-
peak_dict,
239-
std_scaler=STD_SCALER,
240-
show_cursor=True,
241-
)
242-
if not tuple_analyses:
243-
return False
244-
print("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++")
245-
aggregate_analysis_results(tuple_analyses).print()
246-
print("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++")
247-
248-
trades = [trade for trade_tuple in trades for trade in trade_tuple]
249-
trades_dict = dict()
250-
trades_dict["trades"] = [t.dict() for t in trades]
251-
252-
trades_file_writer.write(trades_dict)
253-
analysis = analyze_trades(trades, StrategySummary(False, 1000, 1, "USD", "STC"))
254-
analysis_file_writer.write(analysis.dict())
255159
return True
256160

257161

@@ -328,5 +232,7 @@ def process_ratio(ratio: Ratio, N_DAYS: int, iteration_info: str = ""):
328232
r._process = process_ratio
329233
r.run(N_DAYS=N_DAYS)
330234

235+
print(top_10_ratios)
236+
331237

332238
#! TODO: Use History class, history cache, StatefulDataProcessor

0 commit comments

Comments
 (0)