-
Notifications
You must be signed in to change notification settings - Fork 59
Expand file tree
/
Copy pathcrypto_ma_trade_bot.py
More file actions
executable file
·134 lines (110 loc) · 3.78 KB
/
crypto_ma_trade_bot.py
File metadata and controls
executable file
·134 lines (110 loc) · 3.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#!/usr/bin/env -S uv run --quiet --script
# /// script
# dependencies = [
# "mplfinance",
# "finta",
# "stockstats",
# "python-dotenv",
# "yfinance",
# "persistent-cache@git+https://github.com/namuan/persistent-cache",
# "tqdm",
# "yahoo_earnings_calendar",
# "dataset",
# "flatten-dict",
# "ccxt",
# ]
# ///
"""
Crypto Bot running based on a given strategy
"""
import logging
import mplfinance as mpf
from mplfinance.plotting import make_addplot
from common.analyst import resample_candles
from common.logger import init_logging
from common.steps import TradeSignal, parse_args, procedure
from common.steps_runner import run_forever_with
class ReSampleData:
def run(self, context):
df = context["df"]
context["hourly_df"] = resample_candles(df, "1H")
class CalculateIndicators:
def run(self, context):
df = context["hourly_df"]
context["close"] = df["close"].iloc[-1]
indicators = {}
context["ma_range"] = [3, 25]
for ma in context["ma_range"]:
indicators[f"close_{ma}_ema"] = df[f"close_{ma}_ema"].iloc[-1]
indicators["adx"] = df["dx_14_ema"].iloc[-1]
context["indicators"] = indicators
logging.info(f"Close {context['close']} -> Indicators => {indicators}")
class GenerateChart:
def run(self, context):
df = context["hourly_df"]
args = context["args"]
chart_title = f"{args.coin}_{args.stable_coin}_60m"
context["chart_name"] = chart_title
ma_range = context["ma_range"]
additional_plots = []
for ma in ma_range:
additional_plots.append(
make_addplot(
df[f"close_{ma}_ema"],
type="line",
width=0.5,
)
)
context["chart_file_path"] = chart_file_path = (
f"output/{chart_title.lower()}-mma.png"
)
save = dict(fname=chart_file_path)
fig, axes = mpf.plot(
df,
type="line",
addplot=additional_plots,
savefig=save,
returnfig=True,
)
fig.savefig(save["fname"])
class IdentifyBuySellSignal:
def _if_hit_target(self, actual_order_price, close_price, target_pct):
if actual_order_price < 0:
return False
pct_change = (close_price - actual_order_price) / actual_order_price * 100
sl_hit = "🔴" if pct_change < -1 * target_pct else "🤞"
pt_hit = "✅" if pct_change > target_pct else "🤞"
logging.info(
f"Pct Change: {pct_change:.2f}%, Target Percent: (+/-){target_pct}%, SL Hit {sl_hit}, PT Hit {pt_hit}"
)
return sl_hit or pt_hit
def run(self, context):
indicators = context["indicators"]
args = context["args"]
target_pct = args.target_pct
last_transaction_order_details_price = context[
"last_transaction_order_details_price"
]
close = context["close"]
fast_ema = indicators["close_3_ema"]
slow_ema = indicators["close_25_ema"]
adx = indicators["adx"]
if self._if_hit_target(last_transaction_order_details_price, close, target_pct):
context["signal"] = TradeSignal.SELL
elif close > fast_ema > slow_ema and adx > 35:
context["signal"] = TradeSignal.BUY
else:
context["signal"] = TradeSignal.NO_SIGNAL
logging.info(f"Identified signal => {context.get('signal')}")
def main(args):
init_logging()
identify_trade_procedure = [
ReSampleData(),
CalculateIndicators(),
GenerateChart(),
IdentifyBuySellSignal(),
]
run_forever_with(procedure(identify_trade_procedure), args)
if __name__ == "__main__":
args = parse_args(__doc__)
main(args)