Skip to content

Commit

Permalink
Merge pull request #10 from krypt0ra/develop
Browse files Browse the repository at this point in the history
Preparing to release 1.1.0
  • Loading branch information
alkhachatryan authored Oct 1, 2024
2 parents 6393300 + a4c1810 commit a2464bc
Show file tree
Hide file tree
Showing 9 changed files with 139 additions and 11 deletions.
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: Bug report
about: Create a report to help us improve
title: "[BUG]"
labels: ''
assignees: w1ld3r
assignees: alkhachatryan

---

Expand Down
9 changes: 6 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,21 @@ mkfile_dir := $(dir $(mkfile_path))
NAME = ''

rebuild:
@docker-compose up -d --build && docker-compose start
@docker compose up -d --build && docker compose start

start:
@docker-compose up -d --remove-orphans && docker-compose start
@docker compose up -d --remove-orphans && docker compose start

stop:
@docker-compose stop
@docker compose stop

restart: stop start

connect_app:
@docker exec -it kriptora_station_app bash

connect_redis:
@docker exec -it kriptora_station_redis bash

run:
@docker exec -it kriptora_station_app sh -c "cd app && python app.py"
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ https://github.com/CryptoSignal/Crypto-Signal

https://github.com/w1ld3r/crypto-signal (as for me the best fork for now)

# Contact
If there is a problem related with the project first you can try the official discord of original project:
https://discord.gg/MWTJVFf

Otherwise you can create an issue here with your questions. I'm active in github.

# Installation
I changed a lot of things in the first commit and the part of are docker related changes.
The installation and script running were simplified. In the past if you made small change in the code or even in the config you had to rebuild the whole project. Now you just build, up the container and just run the script in/outside of the container using makefile.
Expand All @@ -22,7 +28,13 @@ That's it. If you changed something in the code or in config - just restart the
# Versioning
There are 3 segments of the version tag X.Y.Z where X represents major releases, Y minor changes - features, Z minor changes - bugfixes

# New features this child-project has
- Fully optimized and fixed dockerization. Run this project just by once command.
- Started since 02 Oct 2024 Redis pubsub notifier is available. You can create multiple connections and use RedisNotifier as a publisher to the channel which was set in config.yml

# TODOs

## These are FIXED!
As you know from the lines above (or if you found this repo probably you already know) that the original project has a lot of, dozens of bugs, git conflicts and much more.
So what I fixed was:
1. ichimoku indicator (pandas error)
Expand Down
9 changes: 8 additions & 1 deletion app/defaults.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# DO NOT EDIT THIS FILE.
# Create a config.yml outside of the app directory
# Create a config.yml at /path/to/station/app/config.yml
# See documentation for details.

settings:
Expand Down Expand Up @@ -62,6 +62,13 @@ notifiers:
enable: null
optional:
template: "{{exchange}}-{{market}}-{{indicator}}-{{indicator_number}} is {{status}}!{{ '\n' -}}"
redis:
required:
host: null
port: null
channel: "pubsub_channel"
optional:
password: null


indicators:
Expand Down
29 changes: 29 additions & 0 deletions app/notification.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
from notifiers.telegram_client import TelegramNotifier
from notifiers.twilio_client import TwilioNotifier
from notifiers.webhook_client import WebhookNotifier
from notifiers.redis_pubsub import RedisPublisher

matplotlib.use('Agg')

Expand Down Expand Up @@ -69,6 +70,7 @@ def __init__(self, notifier_config, indicator_config,
self.telegram_clients = {}
self.webhook_clients = {}
self.stdout_clients = {}
self.redis_pubsubs = {}

enabled_notifiers = list()
self.logger = structlog.get_logger()
Expand Down Expand Up @@ -139,6 +141,18 @@ def __init__(self, notifier_config, indicator_config,
)
enabled_notifiers.append(notifier)

if notifier.startswith('redis'):
self.redis_configured = self._validate_required_config(notifier, notifier_config)

if self.redis_configured:
self.redis_pubsubs[notifier] = RedisPublisher(
host=notifier_config[notifier]['required']['host'],
port=notifier_config[notifier]['required']['port'],
channel=notifier_config[notifier]['required']['channel'],
password=notifier_config[notifier]['optional']['password'],
)
enabled_notifiers.append(notifier)

if notifier.startswith('stdout'):
self.stdout_configured = self._validate_required_config(
notifier, notifier_config)
Expand Down Expand Up @@ -242,6 +256,7 @@ def notify_conditional(self, exchange, market_pair, messages):
self.notify_webhook([new_message], None)
asyncio.run(self.notify_telegram([new_message], None))
self.notify_stdout([new_message])
self.notify_redis([new_message])

def notify_all_messages(self, exchange, market_pair,
candle_period, messages):
Expand All @@ -265,6 +280,7 @@ def notify_all_messages(self, exchange, market_pair,
self.notify_email(messages)
asyncio.run(self.notify_telegram(messages, chart_file))
self.notify_stdout(messages)
self.notify_redis(messages)

def notify_discord(self, messages, chart_file):
"""Send a notification via the discord notifier
Expand Down Expand Up @@ -390,6 +406,19 @@ def notify_webhook(self, messages, chart_file):
for notifier in self.webhook_clients:
self.webhook_clients[notifier].notify(messages, chart_file)

def notify_redis(self, messages):
"""Send a message to redis pubsub
Args:
messages (list): List of messages to send for a specific Exchanche/Market Pair/Candle Period
"""

if not self.redis_configured:
return

for notifier in self.redis_pubsubs:
self.redis_pubsubs[notifier].notify(messages)

def notify_stdout(self, messages):
"""Send a notification via the stdout notifier
Expand Down
67 changes: 67 additions & 0 deletions app/notifiers/redis_pubsub.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import json
import redis
import structlog
from notifiers.utils import NotifierUtils


class RedisPublisher(NotifierUtils):
"""Class for handling Redis pubsub notifications with a specified channel.
"""

# Class-level dictionary to store connections
_redis_connections = {}

def __init__(self, host, port, password, channel):
self._logger = structlog.get_logger()
self._host = host
self._port = port
self._password = password
self._channel = channel
self._connection_key = f"{self._host}:{self._port}:{self._channel}"

# Create a connection if it doesn't exist
self._get_or_create_connection()

def _get_or_create_connection(self):
"""Get an existing connection or create a new one."""
if self._connection_key in self._redis_connections:
return self._redis_connections[self._connection_key]
else:
try:
redis_conn = redis.Redis(
host=self._host,
port=self._port,
password=self._password,
socket_timeout=5, # Add timeout to handle reconnects better
retry_on_timeout=True
)
# Test the connection
redis_conn.ping()
self._redis_connections[self._connection_key] = redis_conn
return redis_conn
except redis.exceptions.ConnectionError as e:
self._logger.error(f"Failed to connect to Redis: {e}")
raise

def notify(self, messages, retrying = False):
"""Sends a message to the Redis pubsub channel defined in the constructor.
Args:
messages (list): The message to send.
retrying (bool): The indicator of message sending retrying. Useful for unexpected connection loss cases
and for getting out from recursion
"""

try:
redis_conn = self._get_or_create_connection()

for message in messages:
# Publish message to the preset channel
redis_conn.publish(self._channel, json.dumps(message))
except redis.exceptions.ConnectionError:
if retrying:
return # Do not repeat itself endless
self._logger.error(f"Connection lost to Redis server {self._connection_key}, attempting reconnect.")
# Clear the connection and retry
del self._redis_connections[self._connection_key]
self.notify(messages, True) # Retry with a new connection
2 changes: 1 addition & 1 deletion app/requirements-step-1.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
numpy>=1.14.0
numpy==1.26.4
Cython>=0.28.2
3 changes: 2 additions & 1 deletion app/requirements-step-2.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ PyYAML>=5.1
newtulipy
matplotlib>=3.0.1
scipy>=1.1.0
numpy>=1.22.2 # not directly required, pinned by Snyk to avoid a vulnerability
numpy==1.26.4
redis # Comment if you dont need redis pubsubs as a notifier
17 changes: 13 additions & 4 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
version: '3'

services:
app:
container_name: kriptora_station_app
build: .
volumes:
- .:/app
networks:
- local-network
- kriptora-station-local-network

# You can comment Redis container creation if you dont need Redis pubsubs as a notifier
redis:
container_name: kriptora_station_redis
image: redis
command: redis-server --requirepass root --bind 0.0.0.0
ports:
- 63790:6379
networks:
- kriptora-station-local-network
networks:
local-network:
kriptora-station-local-network:
driver: bridge
name: kriptora-station-local-network

0 comments on commit a2464bc

Please sign in to comment.