Refactor market into two files
This commit is contained in:
parent
b7c46ba1d3
commit
a41a1e76a5
|
@ -1,6 +1,6 @@
|
|||
from django.test import TestCase
|
||||
|
||||
from core.lib.market import check_conflicting_position, check_existing_position
|
||||
from core.trading.crossfilter import check_conflicting_position, check_existing_position
|
||||
|
||||
|
||||
class MarketTestCase(TestCase):
|
||||
|
|
|
@ -0,0 +1,161 @@
|
|||
from core.exchanges import GenericAPIError
|
||||
from core.util import logs
|
||||
|
||||
log = logs.get_logger(__name__)
|
||||
|
||||
|
||||
def check_existing_position(
|
||||
func: str,
|
||||
position: dict,
|
||||
open_side: str,
|
||||
open_symbol: str,
|
||||
open_units: str,
|
||||
new_side: str,
|
||||
new_symbol: str,
|
||||
trade_side_opposite: str,
|
||||
):
|
||||
# Check if we already have a position for the symbol
|
||||
if open_symbol == new_symbol:
|
||||
# If the live side is the inverse of what we want to do,
|
||||
# we can't open a position
|
||||
if open_side == trade_side_opposite:
|
||||
# If there is a position open, we can't open a new one in the opposite
|
||||
# direction
|
||||
if open_units != "0":
|
||||
# If we have a short on GBP/AUD, we can only place more shorts on
|
||||
# GBP/AUD.
|
||||
if func == "entry":
|
||||
log.debug(
|
||||
f"Refusing to open new {new_side} position on {new_symbol} due "
|
||||
f"to {open_side} position on {open_symbol}"
|
||||
)
|
||||
return {
|
||||
"action": "rejected",
|
||||
"positions": position,
|
||||
}
|
||||
elif func == "exit":
|
||||
log.debug(
|
||||
(
|
||||
f"Found {open_units} units of "
|
||||
f"{open_symbol} on side {trade_side_opposite}"
|
||||
)
|
||||
)
|
||||
# Pass back opposing side so we can close it
|
||||
return {
|
||||
"action": "close",
|
||||
"side": trade_side_opposite,
|
||||
"positions": position,
|
||||
}
|
||||
return False
|
||||
|
||||
|
||||
def check_conflicting_position(
|
||||
func: str,
|
||||
position: dict,
|
||||
open_base: str,
|
||||
open_quote: str,
|
||||
open_side: str,
|
||||
open_symbol: str,
|
||||
open_units: str,
|
||||
new_base: str,
|
||||
new_quote: str,
|
||||
new_side: str,
|
||||
new_symbol: str,
|
||||
trade_side_opposite: str,
|
||||
):
|
||||
if open_base == new_quote or open_quote == new_base:
|
||||
# If we have a long on GBP/AUD, we can only place shorts on XAU/GBP.
|
||||
if open_side != trade_side_opposite:
|
||||
if open_units != "0":
|
||||
# Only do this for entries
|
||||
if func == "entry":
|
||||
log.debug(
|
||||
f"Refusing to open {new_side} position on {new_symbol} due to "
|
||||
f"{open_side} position on {open_symbol}"
|
||||
)
|
||||
return {
|
||||
"action": "rejected",
|
||||
"positions": position,
|
||||
}
|
||||
|
||||
|
||||
def crossfilter(account, new_symbol, new_direction, func):
|
||||
"""
|
||||
Determine if we are betting against ourselves.
|
||||
Checks open positions for the account, rejecting the trade if there is one
|
||||
with an opposite direction to this one.
|
||||
:param account: Account object
|
||||
:param symbol: Symbol
|
||||
:param direction: Direction of the trade
|
||||
:param func: Whether we are checking entries or exits
|
||||
:return: dict of action and opposing position, or False
|
||||
"""
|
||||
try:
|
||||
# Only get the data we need
|
||||
if func == "entry":
|
||||
all_positions = account.client.get_all_positions()
|
||||
else:
|
||||
all_positions = [account.client.get_position_info(new_symbol)]
|
||||
except GenericAPIError as e:
|
||||
if "No position exists for the specified instrument" in str(e):
|
||||
log.debug("No position exists for this symbol")
|
||||
return False
|
||||
else:
|
||||
log.error(f"Error getting position info: {e}")
|
||||
return None
|
||||
if new_direction == "buy":
|
||||
opposing_side = "short"
|
||||
new_side = "long"
|
||||
elif new_direction == "sell":
|
||||
opposing_side = "long"
|
||||
new_side = "short"
|
||||
|
||||
quotes = []
|
||||
new_base, new_quote = new_symbol.split("_")
|
||||
for position in all_positions:
|
||||
# For Forex, get a list of all the quotes.
|
||||
# This is to prevent betting against ourselves.
|
||||
# Consider we have a long position open, EUR/USD, and we want to open a
|
||||
# long position on USD/JPY. If the first goes up, the second one will go
|
||||
# down just as much. We won't make any money.
|
||||
if "_" in position["symbol"]:
|
||||
open_base, open_quote = position["symbol"].split("_")
|
||||
quotes.append(open_quote)
|
||||
|
||||
open_symbol = position["symbol"]
|
||||
open_side = position["side"]
|
||||
open_base, open_quote = open_symbol.split("_")
|
||||
|
||||
# Check if we already have a position
|
||||
existing_position_check = check_existing_position(
|
||||
func=func,
|
||||
position=position,
|
||||
open_side=open_side,
|
||||
open_symbol=open_symbol,
|
||||
open_units=position["units"],
|
||||
new_side=new_side,
|
||||
new_symbol=new_symbol,
|
||||
trade_side_opposite=opposing_side,
|
||||
)
|
||||
if existing_position_check:
|
||||
return existing_position_check
|
||||
|
||||
# Check if we are betting against ourselves
|
||||
conflicting_position_check = check_conflicting_position(
|
||||
func=func,
|
||||
position=position,
|
||||
open_base=open_base,
|
||||
open_quote=open_quote,
|
||||
open_side=open_side,
|
||||
open_symbol=open_symbol,
|
||||
open_units=position["units"],
|
||||
new_base=new_base,
|
||||
new_quote=new_quote,
|
||||
new_side=new_side,
|
||||
new_symbol=new_symbol,
|
||||
trade_side_opposite=opposing_side,
|
||||
)
|
||||
if conflicting_position_check:
|
||||
return conflicting_position_check
|
||||
|
||||
return False
|
|
@ -4,168 +4,12 @@ from decimal import Decimal as D
|
|||
from core.exchanges import GenericAPIError
|
||||
from core.lib.notify import sendmsg
|
||||
from core.models import Account, Strategy, Trade
|
||||
from core.trading.crossfilter import crossfilter
|
||||
from core.util import logs
|
||||
|
||||
log = logs.get_logger(__name__)
|
||||
|
||||
|
||||
def check_existing_position(
|
||||
func: str,
|
||||
position: dict,
|
||||
open_side: str,
|
||||
open_symbol: str,
|
||||
open_units: str,
|
||||
new_side: str,
|
||||
new_symbol: str,
|
||||
trade_side_opposite: str,
|
||||
):
|
||||
# Check if we already have a position for the symbol
|
||||
if open_symbol == new_symbol:
|
||||
# If the live side is the inverse of what we want to do,
|
||||
# we can't open a position
|
||||
if open_side == trade_side_opposite:
|
||||
# If there is a position open, we can't open a new one in the opposite
|
||||
# direction
|
||||
if open_units != "0":
|
||||
# If we have a short on GBP/AUD, we can only place more shorts on
|
||||
# GBP/AUD.
|
||||
if func == "entry":
|
||||
log.debug(
|
||||
f"Refusing to open new {new_side} position on {new_symbol} due "
|
||||
f"to {open_side} position on {open_symbol}"
|
||||
)
|
||||
return {
|
||||
"action": "rejected",
|
||||
"positions": position,
|
||||
}
|
||||
elif func == "exit":
|
||||
log.debug(
|
||||
(
|
||||
f"Found {open_units} units of "
|
||||
f"{open_symbol} on side {trade_side_opposite}"
|
||||
)
|
||||
)
|
||||
# Pass back opposing side so we can close it
|
||||
return {
|
||||
"action": "close",
|
||||
"side": trade_side_opposite,
|
||||
"positions": position,
|
||||
}
|
||||
return False
|
||||
|
||||
|
||||
def check_conflicting_position(
|
||||
func: str,
|
||||
position: dict,
|
||||
open_base: str,
|
||||
open_quote: str,
|
||||
open_side: str,
|
||||
open_symbol: str,
|
||||
open_units: str,
|
||||
new_base: str,
|
||||
new_quote: str,
|
||||
new_side: str,
|
||||
new_symbol: str,
|
||||
trade_side_opposite: str,
|
||||
):
|
||||
if open_base == new_quote or open_quote == new_base:
|
||||
# If we have a long on GBP/AUD, we can only place shorts on XAU/GBP.
|
||||
if open_side != trade_side_opposite:
|
||||
if open_units != "0":
|
||||
# Only do this for entries
|
||||
if func == "entry":
|
||||
log.debug(
|
||||
f"Refusing to open {new_side} position on {new_symbol} due to "
|
||||
f"{open_side} position on {open_symbol}"
|
||||
)
|
||||
return {
|
||||
"action": "rejected",
|
||||
"positions": position,
|
||||
}
|
||||
|
||||
|
||||
def crossfilter(account, new_symbol, new_direction, func):
|
||||
"""
|
||||
Determine if we are betting against ourselves.
|
||||
Checks open positions for the account, rejecting the trade if there is one
|
||||
with an opposite direction to this one.
|
||||
:param account: Account object
|
||||
:param symbol: Symbol
|
||||
:param direction: Direction of the trade
|
||||
:param func: Whether we are checking entries or exits
|
||||
:return: dict of action and opposing position, or False
|
||||
"""
|
||||
try:
|
||||
# Only get the data we need
|
||||
if func == "entry":
|
||||
all_positions = account.client.get_all_positions()
|
||||
else:
|
||||
all_positions = [account.client.get_position_info(new_symbol)]
|
||||
except GenericAPIError as e:
|
||||
if "No position exists for the specified instrument" in str(e):
|
||||
log.debug("No position exists for this symbol")
|
||||
return False
|
||||
else:
|
||||
log.error(f"Error getting position info: {e}")
|
||||
return None
|
||||
if new_direction == "buy":
|
||||
opposing_side = "short"
|
||||
new_side = "long"
|
||||
elif new_direction == "sell":
|
||||
opposing_side = "long"
|
||||
new_side = "short"
|
||||
|
||||
quotes = []
|
||||
new_base, new_quote = new_symbol.split("_")
|
||||
for position in all_positions:
|
||||
# For Forex, get a list of all the quotes.
|
||||
# This is to prevent betting against ourselves.
|
||||
# Consider we have a long position open, EUR/USD, and we want to open a
|
||||
# long position on USD/JPY. If the first goes up, the second one will go
|
||||
# down just as much. We won't make any money.
|
||||
if "_" in position["symbol"]:
|
||||
open_base, open_quote = position["symbol"].split("_")
|
||||
quotes.append(open_quote)
|
||||
|
||||
open_symbol = position["symbol"]
|
||||
open_side = position["side"]
|
||||
open_base, open_quote = open_symbol.split("_")
|
||||
|
||||
# Check if we already have a position
|
||||
existing_position_check = check_existing_position(
|
||||
func=func,
|
||||
position=position,
|
||||
open_side=open_side,
|
||||
open_symbol=open_symbol,
|
||||
open_units=position["units"],
|
||||
new_side=new_side,
|
||||
new_symbol=new_symbol,
|
||||
trade_side_opposite=opposing_side,
|
||||
)
|
||||
if existing_position_check:
|
||||
return existing_position_check
|
||||
|
||||
# Check if we are betting against ourselves
|
||||
conflicting_position_check = check_conflicting_position(
|
||||
func=func,
|
||||
position=position,
|
||||
open_base=open_base,
|
||||
open_quote=open_quote,
|
||||
open_side=open_side,
|
||||
open_symbol=open_symbol,
|
||||
open_units=position["units"],
|
||||
new_base=new_base,
|
||||
new_quote=new_quote,
|
||||
new_side=new_side,
|
||||
new_symbol=new_symbol,
|
||||
trade_side_opposite=opposing_side,
|
||||
)
|
||||
if conflicting_position_check:
|
||||
return conflicting_position_check
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def get_pair(account, base, quote, invert=False):
|
||||
"""
|
||||
Get the pair for the given account and currencies.
|
|
@ -9,9 +9,9 @@ from rest_framework.parsers import JSONParser
|
|||
from rest_framework.views import APIView
|
||||
|
||||
from core.forms import HookForm
|
||||
from core.lib import market
|
||||
from core.lib.schemas.drakdoo_s import DrakdooCallback
|
||||
from core.models import Callback, Hook, Signal
|
||||
from core.trading import market
|
||||
from core.util import logs
|
||||
from core.views import ObjectCreate, ObjectDelete, ObjectList, ObjectUpdate
|
||||
|
||||
|
|
Loading…
Reference in New Issue