This commit is contained in:
2025-11-09 22:07:14 +01:00
parent 596fcde0bf
commit 1165060bc0
27 changed files with 2213 additions and 107 deletions

View File

@ -0,0 +1,2 @@
# This file can be empty.
# It tells Python that 'position_logic' is a directory containing modules.

View File

@ -0,0 +1,31 @@
from abc import ABC, abstractmethod
import logging
class BasePositionLogic(ABC):
"""
Abstract base class for all strategy-specific position logic.
Defines the interface for how the PositionManager interacts with logic modules.
"""
def __init__(self, strategy_name: str, send_order_callback, log_trade_callback):
self.strategy_name = strategy_name
self.send_order = send_order_callback
self.log_trade = log_trade_callback
logging.info(f"Initialized position logic for '{strategy_name}'")
@abstractmethod
def handle_signal(self, signal_data: dict, current_strategy_positions: dict) -> dict:
"""
The core logic method. This is called by the PositionManager when a
new signal arrives for this strategy.
Args:
signal_data: The full signal dictionary from the strategy.
current_strategy_positions: A dict of this strategy's current positions,
keyed by coin (e.g., {"BTC": {"side": "long", ...}}).
Returns:
A dictionary representing the new state for the *specific coin* in the
signal (e.g., {"side": "long", "size": 0.1}).
Return None to indicate the position for this coin should be closed/removed.
"""
pass

View File

@ -0,0 +1,83 @@
import logging
from position_logic.base_logic import BasePositionLogic
class DefaultFlipLogic(BasePositionLogic):
"""
The standard "flip-on-signal" logic used by most simple strategies
(SMA, MA Cross, and even the per-coin Copy Trader signals).
- BUY signal: Closes any short, opens a long.
- SELL signal: Closes any long, opens a short.
- FLAT signal: Closes any open position.
"""
def handle_signal(self, signal_data: dict, current_strategy_positions: dict) -> dict:
"""
Processes a BUY, SELL, or FLAT signal and issues the necessary orders
to flip or open a position.
"""
name = self.strategy_name
params = signal_data['config']['parameters']
coin = signal_data['coin']
desired_signal = signal_data['signal']
signal_price = signal_data.get('signal_price', 0)
size = params.get('size')
leverage_long = int(params.get('leverage_long', 2))
leverage_short = int(params.get('leverage_short', 2))
agent_name = signal_data['config'].get("agent", "default").lower()
# --- This logic now correctly targets a specific coin ---
current_position = current_strategy_positions.get(coin)
new_position_state = None # Return None to close position
if desired_signal == "BUY" or desired_signal == "INIT_BUY":
new_position_state = {"coin": coin, "side": "long", "size": size}
if not current_position:
logging.warning(f"[{name}]-[{coin}] ACTION: Setting leverage to {leverage_long}x and opening LONG.")
self.send_order(agent_name, "update_leverage", coin, is_buy=True, size=leverage_long)
self.send_order(agent_name, "market_open", coin, is_buy=True, size=size)
self.log_trade(strategy=name, coin=coin, action="OPEN_LONG", price=signal_price, size=size, signal=desired_signal)
elif current_position['side'] == 'short':
logging.warning(f"[{name}]-[{coin}] ACTION: Closing SHORT and opening LONG with {leverage_long}x leverage.")
self.send_order(agent_name, "update_leverage", coin, is_buy=True, size=leverage_long)
self.send_order(agent_name, "market_open", coin, is_buy=True, size=current_position['size'], reduce_only=True)
self.log_trade(strategy=name, coin=coin, action="CLOSE_SHORT", price=signal_price, size=current_position['size'], signal=desired_signal)
self.send_order(agent_name, "market_open", coin, is_buy=True, size=size)
self.log_trade(strategy=name, coin=coin, action="OPEN_LONG", price=signal_price, size=size, signal=desired_signal)
else: # Already long, do nothing
logging.info(f"[{name}]-[{coin}] INFO: Already LONG, no action taken.")
new_position_state = current_position # State is unchanged
elif desired_signal == "SELL" or desired_signal == "INIT_SELL":
new_position_state = {"coin": coin, "side": "short", "size": size}
if not current_position:
logging.warning(f"[{name}]-[{coin}] ACTION: Setting leverage to {leverage_short}x and opening SHORT.")
self.send_order(agent_name, "update_leverage", coin, is_buy=False, size=leverage_short)
self.send_order(agent_name, "market_open", coin, is_buy=False, size=size)
self.log_trade(strategy=name, coin=coin, action="OPEN_SHORT", price=signal_price, size=size, signal=desired_signal)
elif current_position['side'] == 'long':
logging.warning(f"[{name}]-[{coin}] ACTION: Closing LONG and opening SHORT with {leverage_short}x leverage.")
self.send_order(agent_name, "update_leverage", coin, is_buy=False, size=leverage_short)
self.send_order(agent_name, "market_open", coin, is_buy=False, size=current_position['size'], reduce_only=True)
self.log_trade(strategy=name, coin=coin, action="CLOSE_LONG", price=signal_price, size=current_position['size'], signal=desired_signal)
self.send_order(agent_name, "market_open", coin, is_buy=False, size=size)
self.log_trade(strategy=name, coin=coin, action="OPEN_SHORT", price=signal_price, size=size, signal=desired_signal)
else: # Already short, do nothing
logging.info(f"[{name}]-[{coin}] INFO: Already SHORT, no action taken.")
new_position_state = current_position # State is unchanged
elif desired_signal == "FLAT":
if current_position:
logging.warning(f"[{name}]-[{coin}] ACTION: Close {current_position['side']} position.")
is_buy = current_position['side'] == 'short' # To close a short, we buy
self.send_order(agent_name, "market_open", coin, is_buy=is_buy, size=current_position['size'], reduce_only=True)
self.log_trade(strategy=name, coin=coin, action=f"CLOSE_{current_position['side'].upper()}", price=signal_price, size=current_position['size'], signal=desired_signal)
# new_position_state is already None, which will remove it
return new_position_state