Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

custom buy/sell strategies #13

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
168 changes: 160 additions & 8 deletions assets.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,175 @@
from typing import List
from dataclasses import dataclass
from abc import ABC, abstractmethod
import datetime


@dataclass
class Candle:
"""
Represents a single candlestick data point.
"""
start: int
low: float
high: float
open: float
close: float
volume: float


def parse_candles(candle_data: List[dict]) -> List[Candle]:
"""
Parses an array of JSON-like candle data into an array of Candle objects.

Args:
candle_data (List[dict]): A list of dictionaries, where each dictionary represents a single candle.

Returns:
List[Candle]: A list of Candle objects.
"""
candles = []
for candle_dict in candle_data:
candle = Candle(
start=int(candle_dict["start"]),
low=float(candle_dict["low"]),
high=float(candle_dict["high"]),
open=float(candle_dict["open"]),
close=float(candle_dict["close"]),
volume=float(candle_dict["volume"])
)
candles.append(candle)
return candles


class BuyStrategy(ABC):
"""
Base class for all buy strategies.
"""
@abstractmethod
def should_buy(self, logger, candles: [Candle]):
"""
Determines whether the asset should be sold based on the strategy.

Args:
asset_data (dict): A dictionary containing the current asset data.

Returns:
bool: True if the asset should be sold, False otherwise.
"""
pass


class BuyTheDipStrategy(ABC):
"""
Buy strategy to buy the dip.
"""

def __init__(self, candle_size, green_candles_in_a_row):
self.candle_size = candle_size
self.green_candles_in_a_row = green_candles_in_a_row

def should_buy(self, logger, candles: [Candle]):
"""
Determines whether the asset should be bought based on the Buy the Dip strategy.
"""
green_candle_count = 0
for candle in candles:
candle_time = datetime.utcfromtimestamp(int(candle.start))
candle_date = candle_time.strftime("%Y-%m-%d %H:%M:%S")
is_green = candle["close"] > candle["open"]
if is_green:
logger.info(f"found green candle at {candle_date}")
green_candle_count += 1
else:
logger.info(f"found red candle at {candle_date}")
break

return green_candle_count >= self.green_candles_in_a_row


class SellStrategy(ABC):
"""
Base class for all sell strategies.
"""
@abstractmethod
def should_sell(self, logger, candles: [Candle]):
"""
Determines whether the asset should be sold based on the strategy.

Args:
asset_data (dict): A dictionary containing the current asset data.

Returns:
bool: True if the asset should be sold, False otherwise.
"""
pass


class MaximizeProfitSellStrategy(SellStrategy):
"""
Sell strategy that aims to maximize profit.
"""

def __init__(self, candle_size, red_candles_in_a_row):
self.candle_size = candle_size
self.red_candles_in_a_row = red_candles_in_a_row

def should_sell(self, logger, candles: [Candle]):
"""
Determines whether the asset should be sold based on the Maximize Profit strategy.
"""
red_candle_count = 0
for candle in candles:
candle_time = datetime.utcfromtimestamp(int(candle.start))
candle_date = candle_time.strftime("%Y-%m-%d %H:%M:%S")
is_red = candle["close"] < candle["open"]
if is_red:
logger.info(f"found red candle at {candle_date}")
red_candle_count += 1
else:
logger.info(f"found green candle at {candle_date}")
break

return red_candle_count >= self.red_candles_in_a_row


class ImmediateSellStrategy(SellStrategy):
"""
Sell strategy that sells the asset immediately.
"""

def should_sell(self, logger, candles: [Candle]):
"""
Determines whether the asset should be sold based on the Immediate Sell strategy.
"""
return True


class Asset():
"""Base class for all assets"""

def __init__(
self,
name: str,
symbol: str,
account_id: str,
amount_to_buy: float,
buy_price_percentage_change_threshold: float,
sell_price_percentage_change_threshold: float,
max_open_buys: int):
self,
name: str,
symbol: str,
account_id: str,
amount_to_buy: float,
buy_price_percentage_change_threshold: float,
sell_price_percentage_change_threshold: float,
max_open_buys: int,
buy_strategy: BuyStrategy,
sell_strategy: SellStrategy

):
self.name = name
self.symbol = symbol
self.account_id = account_id
self.amount_to_buy = amount_to_buy
self.buy_price_percentage_change_threshold = buy_price_percentage_change_threshold
self.sell_price_percentage_change_threshold = sell_price_percentage_change_threshold
self.max_open_buys = max_open_buys
self.buy_strategy = buy_strategy
self.sell_strategy = sell_strategy

def __str__(self):
return f'{self.name} @ {self.price}'
Expand Down
7 changes: 5 additions & 2 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@
"sell_threshold": 12.0,
"max_open_buys": 5,
"buy_strategy": {
"candle_size": "ONE_HOUR",
"green_candles_in_a_row": 1
"type": "BUY_THE_DIP",
"BUY_THE_DIP": {
"candle_size": "ONE_HOUR",
"green_candles_in_a_row": 1
}
},
"sell_strategy": {
"type": "MAXIMIZE_PROFIT",
Expand Down
15 changes: 14 additions & 1 deletion decisions.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from enum import Enum
from datetime import datetime
import uuid
from dataclasses import dataclass


class Enviorment(Enum):
Expand Down Expand Up @@ -85,7 +86,8 @@ def get_attributes(self):
attrs["context"] = self.context.get_attributes()
return attrs

def uuid(self):
@property
def uuid(self) -> str:
return self.uuid


Expand Down Expand Up @@ -195,3 +197,14 @@ def __init__(
@property
def action(self):
return DecisionType.BEST_MATCH_BELOW_THRESHOLD.value


@dataclass
class BuyDecisionSummary():
uuid: str
price: float
amount: float
value: float

def get_percent_delta(self, current_price: float) -> float:
return ((current_price - self.price) / self.price) * 100
Loading